# HG changeset patch # User Frank Benoit # Date 1199661074 -3600 # Node ID 7b1ca4eb57638d8d1587a9f98b1ae980c753c9e0 # Parent 3d9bbe0a83a0e1dbef955fc32f161ccfa710ef3d file format png diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/dwthelper/BufferedInputStream.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dwthelper/BufferedInputStream.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,107 @@ +/** + * Authors: Frank Benoit + */ +module dwt.dwthelper.BufferedInputStream; + +import dwt.dwthelper.InputStream; +import dwt.dwthelper.utils; + +import tango.core.Exception; + +public class BufferedInputStream : dwt.dwthelper.InputStream.InputStream { + + alias dwt.dwthelper.InputStream.InputStream.read read; + + private const int defaultSize = 8192; + protected byte[] buf; + protected int count = 0; /// The index one greater than the index of the last valid byte in the buffer. + protected int pos = 0; /// The current position in the buffer. + protected int markpos = (-1); + protected int marklimit; + dwt.dwthelper.InputStream.InputStream istr; + + public this ( dwt.dwthelper.InputStream.InputStream istr ){ + this( istr, defaultSize ); + } + + public this ( dwt.dwthelper.InputStream.InputStream istr, int size ){ + this.istr = istr; + if( size <= 0 ){ + throw new IllegalArgumentException( "Buffer size <= 0" ); + } + buf.length = size; + } + + private InputStream getAndCheckIstr(){ + InputStream res = istr; + if( res is null ){ + throw new IOException( "Stream closed" ); + } + return res; + } + private byte[] getAndCheckBuf(){ + byte[] res = buf; + if( res is null ){ + throw new IOException( "Stream closed" ); + } + return res; + } + private void fill(){ + assert( pos == count ); + pos = 0; + count = 0; + int count = getAndCheckIstr().read( buf ); + if( count < 0 ){ + count = 0; + istr = null; + } + } + public synchronized int read(){ + if( pos >= count ){ + fill(); + if( pos >= count ){ + return -1; + } + } + return getAndCheckBuf()[pos++] & 0xFF; + } + + public synchronized int read( byte[] b, int off, int len ){ + return super.read( b, off, len ); + } + + public synchronized long skip( long n ){ + return this.istr.skip(n); + } + + public synchronized int available(){ + int istr_avail = 0; + if( istr !is null ){ + istr_avail = istr.available(); + } + return istr_avail + (count - pos); + } + + public synchronized void mark( int readlimit ){ + implMissing( __FILE__, __LINE__ ); + this.istr.mark( readlimit ); + } + + public synchronized void reset(){ + implMissing( __FILE__, __LINE__ ); + this.istr.reset(); + } + + public bool markSupported(){ + implMissing( __FILE__, __LINE__ ); + return false; + } + + public void close(){ + this.istr.close(); + } + + +} + + diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/FileFormat.d --- a/dwt/internal/image/FileFormat.d Sun Jan 06 22:54:14 2008 +0100 +++ b/dwt/internal/image/FileFormat.d Mon Jan 07 00:11:14 2008 +0100 @@ -26,26 +26,11 @@ import dwt.internal.image.TIFFFileFormat; import dwt.internal.image.OS2BMPFileFormat; import dwt.internal.image.JPEGFileFormat; -/+ import dwt.internal.image.PNGFileFormat; -+/ import tango.core.Exception; import tango.core.Tuple; -class Tmp : FileFormat{ - bool isFileFormat(LEDataInputStream stream){ - return false; - } - ImageData[] loadFromByteStream(){ - return null; - } - void unloadIntoByteStream(ImageLoader loader){ - } -} -class PNGFileFormat : Tmp{ -} - /** * Abstract factory class for loading/unloading images from files or streams * in various image file formats. diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PNGFileFormat.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PNGFileFormat.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,591 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PNGFileFormat; + + +import dwt.SWT; +import dwt.graphics.ImageData; +import dwt.graphics.ImageLoaderEvent; +import dwt.graphics.ImageLoader; +import dwt.graphics.PaletteData; +import dwt.internal.Compatibility; +import dwt.internal.image.FileFormat; +import dwt.internal.image.PngIhdrChunk; +import dwt.internal.image.PngPlteChunk; +import dwt.internal.image.PngChunkReader; +import dwt.internal.image.PngChunk; +import dwt.internal.image.PngTrnsChunk; +import dwt.internal.image.PngIdatChunk; +import dwt.internal.image.PngEncoder; +import dwt.internal.image.PngInputStream; +import dwt.internal.image.PngDecodingDataStream; + +import dwt.dwthelper.BufferedInputStream; + +import tango.core.Exception; + +final class PNGFileFormat : FileFormat { + static final int SIGNATURE_LENGTH = 8; + static final int PRIME = 65521; + PngIhdrChunk headerChunk; + PngPlteChunk paletteChunk; + ImageData imageData; + byte[] data; + byte[] alphaPalette; + byte headerByte1; + byte headerByte2; + int adler; + +/** + * Skip over signature data. This has already been + * verified in isFileFormat(). + */ +void readSignature() { + byte[] signature = new byte[SIGNATURE_LENGTH]; + inputStream.read(signature); +} +/** + * Load the PNG image from the byte stream. + */ +ImageData[] loadFromByteStream() { + try { + readSignature(); + PngChunkReader chunkReader = new PngChunkReader(inputStream); + headerChunk = chunkReader.getIhdrChunk(); + int width = headerChunk.getWidth(), height = headerChunk.getHeight(); + if (width <= 0 || height <= 0) SWT.error(SWT.ERROR_INVALID_IMAGE); + int imageSize = getAlignedBytesPerRow() * height; + data = new byte[imageSize]; + imageData = ImageData.internal_new( + width, + height, + headerChunk.getSwtBitsPerPixel(), + new PaletteData(0, 0, 0), + 4, + data, + 0, + null, + null, + -1, + -1, + SWT.IMAGE_PNG, + 0, + 0, + 0, + 0); + + if (headerChunk.usesDirectColor()) { + imageData.palette = headerChunk.getPaletteData(); + } + + // Read and process chunks until the IEND chunk is encountered. + while (chunkReader.hasMoreChunks()) { + readNextChunk(chunkReader); + } + + return [imageData]; + } catch (IOException e) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + return null; + } +} +/** + * Read and handle the next chunk of data from the + * PNG file. + */ +void readNextChunk(PngChunkReader chunkReader) { + PngChunk chunk = chunkReader.readNextChunk(); + switch (chunk.getChunkType()) { + case PngChunk.CHUNK_IEND: + break; + case PngChunk.CHUNK_PLTE: + if (!headerChunk.usesDirectColor()) { + paletteChunk = cast(PngPlteChunk) chunk; + imageData.palette = paletteChunk.getPaletteData(); + } + break; + case PngChunk.CHUNK_tRNS: + PngTrnsChunk trnsChunk = cast(PngTrnsChunk) chunk; + if (trnsChunk.getTransparencyType(headerChunk) == + PngTrnsChunk.TRANSPARENCY_TYPE_PIXEL) + { + imageData.transparentPixel = + trnsChunk.getSwtTransparentPixel(headerChunk); + } else { + alphaPalette = trnsChunk.getAlphaValues(headerChunk, paletteChunk); + int transparentCount = 0, transparentPixel = -1; + for (int i = 0; i < alphaPalette.length; i++) { + if ((alphaPalette[i] & 0xFF) != 255) { + transparentCount++; + transparentPixel = i; + } + } + if (transparentCount == 0) { + alphaPalette = null; + } else if (transparentCount == 1 && alphaPalette[transparentPixel] == 0) { + alphaPalette = null; + imageData.transparentPixel = transparentPixel; + } + } + break; + case PngChunk.CHUNK_IDAT: + if (chunkReader.readPixelData()) { + // All IDAT chunks in an image file must be + // sequential. If the pixel data has already + // been read and another IDAT block is encountered, + // then this is an invalid image. + SWT.error(SWT.ERROR_INVALID_IMAGE); + } else { + // Read in the pixel data for the image. This should + // go through all the image's IDAT chunks. + PngIdatChunk dataChunk = cast(PngIdatChunk) chunk; + readPixelData(dataChunk, chunkReader); + } + break; + default: + if (chunk.isCritical()) { + // All critical chunks must be supported. + SWT.error(SWT.ERROR_NOT_IMPLEMENTED); + } + } +} +void unloadIntoByteStream(ImageLoader loader) { + PngEncoder encoder = new PngEncoder(loader); + encoder.encode(outputStream); +} +bool isFileFormat(LEDataInputStream stream) { + try { + byte[] signature = new byte[SIGNATURE_LENGTH]; + stream.read(signature); + stream.unread(signature); + if ((signature[0] & 0xFF) != 137) return false; //137 + if ((signature[1] & 0xFF) != 80) return false; //P + if ((signature[2] & 0xFF) != 78) return false; //N + if ((signature[3] & 0xFF) != 71) return false; //G + if ((signature[4] & 0xFF) != 13) return false; // + if ((signature[5] & 0xFF) != 10) return false; // + if ((signature[6] & 0xFF) != 26) return false; // + if ((signature[7] & 0xFF) != 10) return false; // + return true; + } catch (Exception e) { + return false; + } +} +/** + * SWT does not support 16-bit depths. If this image uses + * 16-bit depths, convert the data to an 8-bit depth. + */ +byte[] validateBitDepth(byte[] data) { + if (headerChunk.getBitDepth() > 8) { + byte[] result = new byte[data.length / 2]; + compress16BitDepthTo8BitDepth(data, 0, result, 0, result.length); + return result; + } else { + return data; + } +} +/** + * SWT does not support greyscale as a color type. For + * plain grayscale, we create a palette. For Grayscale + * with Alpha, however, we need to convert the pixels + * to use RGB values. + * Note: This method assumes that the bit depth of the + * data has already been restricted to 8 or less. + */ +void setPixelData(byte[] data, ImageData imageData) { + switch (headerChunk.getColorType()) { + case PngIhdrChunk.COLOR_TYPE_GRAYSCALE_WITH_ALPHA: + { + int width = imageData.width; + int height = imageData.height; + int destBytesPerLine = imageData.bytesPerLine; + /* + * If the image uses 16-bit depth, it is converted + * to an 8-bit depth image. + */ + int srcBytesPerLine = getAlignedBytesPerRow(); + if (headerChunk.getBitDepth() > 8) srcBytesPerLine /= 2; + + byte[] rgbData = new byte[destBytesPerLine * height]; + byte[] alphaData = new byte[width * height]; + for (int y = 0; y < height; y++) { + int srcIndex = srcBytesPerLine * y; + int destIndex = destBytesPerLine * y; + int destAlphaIndex = width * y; + for (int x = 0; x < width; x++) { + byte grey = data[srcIndex]; + byte alpha = data[srcIndex + 1]; + rgbData[destIndex + 0] = grey; + rgbData[destIndex + 1] = grey; + rgbData[destIndex + 2] = grey; + alphaData[destAlphaIndex] = alpha; + srcIndex += 2; + destIndex += 3; + destAlphaIndex++; + } + } + imageData.data = rgbData; + imageData.alphaData = alphaData; + break; + } + case PngIhdrChunk.COLOR_TYPE_RGB_WITH_ALPHA: + { + int width = imageData.width; + int height = imageData.height; + int destBytesPerLine = imageData.bytesPerLine; + int srcBytesPerLine = getAlignedBytesPerRow(); + /* + * If the image uses 16-bit depth, it is converted + * to an 8-bit depth image. + */ + if (headerChunk.getBitDepth() > 8) srcBytesPerLine /= 2; + + byte[] rgbData = new byte[destBytesPerLine * height]; + byte[] alphaData = new byte[width * height]; + for (int y = 0; y < height; y++) { + int srcIndex = srcBytesPerLine * y; + int destIndex = destBytesPerLine * y; + int destAlphaIndex = width * y; + for (int x = 0; x < width; x++) { + rgbData[destIndex + 0] = data[srcIndex + 0]; + rgbData[destIndex + 1] = data[srcIndex + 1]; + rgbData[destIndex + 2] = data[srcIndex + 2]; + alphaData[destAlphaIndex] = data[srcIndex + 3]; + srcIndex += 4; + destIndex += 3; + destAlphaIndex++; + } + } + imageData.data = rgbData; + imageData.alphaData = alphaData; + break; + } + case PngIhdrChunk.COLOR_TYPE_RGB: + imageData.data = data; + break; + case PngIhdrChunk.COLOR_TYPE_PALETTE: + imageData.data = data; + if (alphaPalette != null) { + int size = imageData.width * imageData.height; + byte[] alphaData = new byte[size]; + byte[] pixelData = new byte[size]; + imageData.getPixels(0, 0, size, pixelData, 0); + for (int i = 0; i < pixelData.length; i++) { + alphaData[i] = alphaPalette[pixelData[i] & 0xFF]; + } + imageData.alphaData = alphaData; + } + break; + default: + imageData.data = data; + break; + } +} +/** + * PNG supports some color types and bit depths that are + * unsupported by SWT. If the image uses an unsupported + * color type (either of the gray scale types) or bit + * depth (16), convert the data to an SWT-supported + * format. Then assign the data into the ImageData given. + */ +void setImageDataValues(byte[] data, ImageData imageData) { + byte[] result = validateBitDepth(data); + setPixelData(result, imageData); +} +/** + * Read the image data from the data stream. This must handle + * decoding the data, filtering, and interlacing. + */ +void readPixelData(PngIdatChunk chunk, PngChunkReader chunkReader) { + InputStream stream = new PngInputStream(chunk, chunkReader); + //TEMPORARY CODE + //PORTING_FIXME + bool use3_2 = false;//System.getProperty("dwt.internal.image.PNGFileFormat_3.2") != null; + InputStream inflaterStream = use3_2 ? null : Compatibility.newInflaterInputStream(stream); + if (inflaterStream != null) { + stream = new BufferedInputStream(inflaterStream); + } else { + stream = new PngDecodingDataStream(stream); + } + int interlaceMethod = headerChunk.getInterlaceMethod(); + if (interlaceMethod == PngIhdrChunk.INTERLACE_METHOD_NONE) { + readNonInterlacedImage(stream); + } else { + readInterlacedImage(stream); + } + /* + * InflaterInputStream does not consume all bytes in the stream + * when it is closed. This may leave unread IDAT chunks. The fix + * is to read all available bytes before closing it. + */ + while (stream.available() > 0) stream.read(); + stream.close(); +} +/** + * Answer the number of bytes in a word-aligned row of pixel data. + */ +int getAlignedBytesPerRow() { + return ((getBytesPerRow(headerChunk.getWidth()) + 3) / 4) * 4; +} +/** + * Answer the number of bytes in each row of the image + * data. Each PNG row is byte-aligned, so images with bit + * depths less than a byte may have unused bits at the + * end of each row. The value of these bits is undefined. + */ +int getBytesPerRow() { + return getBytesPerRow(headerChunk.getWidth()); +} +/** + * Answer the number of bytes needed to represent a pixel. + * This value depends on the image's color type and bit + * depth. + * Note that this method rounds up if an image's pixel size + * isn't byte-aligned. + */ +int getBytesPerPixel() { + int bitsPerPixel = headerChunk.getBitsPerPixel(); + return (bitsPerPixel + 7) / 8; +} +/** + * Answer the number of bytes in a row of the given pixel + * width. Each row is byte-aligned, so images with bit + * depths less than a byte may have unused bits at the + * end of each row. The value of these bits is undefined. + */ +int getBytesPerRow(int rowWidthInPixels) { + int bitsPerPixel = headerChunk.getBitsPerPixel(); + int bitsPerRow = bitsPerPixel * rowWidthInPixels; + int bitsPerByte = 8; + return (bitsPerRow + (bitsPerByte - 1)) / bitsPerByte; +} +/** + * 1. Read one of the seven frames of interlaced data. + * 2. Update the imageData. + * 3. Notify the image loader's listeners of the frame load. + */ +void readInterlaceFrame( + InputStream inputStream, + int rowInterval, + int columnInterval, + int startRow, + int startColumn, + int frameCount) +{ + int width = headerChunk.getWidth(); + int alignedBytesPerRow = getAlignedBytesPerRow(); + int height = headerChunk.getHeight(); + if (startRow >= height || startColumn >= width) return; + + int pixelsPerRow = (width - startColumn + columnInterval - 1) / columnInterval; + int bytesPerRow = getBytesPerRow(pixelsPerRow); + byte[] row1 = new byte[bytesPerRow]; + byte[] row2 = new byte[bytesPerRow]; + byte[] currentRow = row1; + byte[] lastRow = row2; + for (int row = startRow; row < height; row += rowInterval) { + byte filterType = cast(byte)inputStream.read(); + int read = 0; + while (read != bytesPerRow) { + read += inputStream.read(currentRow, read, bytesPerRow - read); + } + filterRow(currentRow, lastRow, filterType); + if (headerChunk.getBitDepth() >= 8) { + int bytesPerPixel = getBytesPerPixel(); + int dataOffset = (row * alignedBytesPerRow) + (startColumn * bytesPerPixel); + for (int rowOffset = 0; rowOffset < currentRow.length; rowOffset += bytesPerPixel) { + for (int byteOffset = 0; byteOffset < bytesPerPixel; byteOffset++) { + data[dataOffset + byteOffset] = currentRow[rowOffset + byteOffset]; + } + dataOffset += (columnInterval * bytesPerPixel); + } + } else { + int bitsPerPixel = headerChunk.getBitDepth(); + int pixelsPerByte = 8 / bitsPerPixel; + int column = startColumn; + int rowBase = row * alignedBytesPerRow; + int valueMask = 0; + for (int i = 0; i < bitsPerPixel; i++) { + valueMask <<= 1; + valueMask |= 1; + } + int maxShift = 8 - bitsPerPixel; + for (int byteOffset = 0; byteOffset < currentRow.length; byteOffset++) { + for (int bitOffset = maxShift; bitOffset >= 0; bitOffset -= bitsPerPixel) { + if (column < width) { + int dataOffset = rowBase + (column * bitsPerPixel / 8); + int value = (currentRow[byteOffset] >> bitOffset) & valueMask; + int dataShift = maxShift - (bitsPerPixel * (column % pixelsPerByte)); + data[dataOffset] |= value << dataShift; + } + column += columnInterval; + } + } + } + currentRow = (currentRow == row1) ? row2 : row1; + lastRow = (lastRow == row1) ? row2 : row1; + } + setImageDataValues(data, imageData); + fireInterlacedFrameEvent(frameCount); +} +/** + * Read the pixel data for an interlaced image from the + * data stream. + */ +void readInterlacedImage(InputStream inputStream) { + readInterlaceFrame(inputStream, 8, 8, 0, 0, 0); + readInterlaceFrame(inputStream, 8, 8, 0, 4, 1); + readInterlaceFrame(inputStream, 8, 4, 4, 0, 2); + readInterlaceFrame(inputStream, 4, 4, 0, 2, 3); + readInterlaceFrame(inputStream, 4, 2, 2, 0, 4); + readInterlaceFrame(inputStream, 2, 2, 0, 1, 5); + readInterlaceFrame(inputStream, 2, 1, 1, 0, 6); +} +/** + * Fire an event to let listeners know that an interlaced + * frame has been loaded. + * finalFrame should be true if the image has finished + * loading, false if there are more frames to come. + */ +void fireInterlacedFrameEvent(int frameCount) { + if (loader.hasListeners()) { + ImageData image = cast(ImageData) imageData.clone(); + bool finalFrame = frameCount == 6; + loader.notifyListeners(new ImageLoaderEvent(loader, image, frameCount, finalFrame)); + } +} +/** + * Read the pixel data for a non-interlaced image from the + * data stream. + * Update the imageData to reflect the new data. + */ +void readNonInterlacedImage(InputStream inputStream) { + int dataOffset = 0; + int alignedBytesPerRow = getAlignedBytesPerRow(); + int bytesPerRow = getBytesPerRow(); + byte[] row1 = new byte[bytesPerRow]; + byte[] row2 = new byte[bytesPerRow]; + byte[] currentRow = row1; + byte[] lastRow = row2; + int height = headerChunk.getHeight(); + for (int row = 0; row < height; row++) { + byte filterType = cast(byte)inputStream.read(); + int read = 0; + while (read != bytesPerRow) { + read += inputStream.read(currentRow, read, bytesPerRow - read); + } + filterRow(currentRow, lastRow, filterType); + data[ dataOffset .. dataOffset+bytesPerRow ] = currentRow[ 0 .. bytesPerRow ]; + dataOffset += alignedBytesPerRow; + currentRow = (currentRow == row1) ? row2 : row1; + lastRow = (lastRow == row1) ? row2 : row1; + } + setImageDataValues(data, imageData); +} +/** + * SWT does not support 16-bit depth color formats. + * Convert the 16-bit data to 8-bit data. + * The correct way to do this is to multiply each + * 16 bit value by the value: + * (2^8 - 1) / (2^16 - 1). + * The fast way to do this is just to drop the low + * byte of the 16-bit value. + */ +static void compress16BitDepthTo8BitDepth( + byte[] source, + int sourceOffset, + byte[] destination, + int destinationOffset, + int numberOfValues) +{ + //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1); + for (int i = 0; i < numberOfValues; i++) { + int sourceIndex = sourceOffset + (2 * i); + int destinationIndex = destinationOffset + i; + //int value = (source[sourceIndex] << 8) | source[sourceIndex + 1]; + //byte compressedValue = (byte)(value * multiplier); + byte compressedValue = source[sourceIndex]; + destination[destinationIndex] = compressedValue; + } +} +/** + * SWT does not support 16-bit depth color formats. + * Convert the 16-bit data to 8-bit data. + * The correct way to do this is to multiply each + * 16 bit value by the value: + * (2^8 - 1) / (2^16 - 1). + * The fast way to do this is just to drop the low + * byte of the 16-bit value. + */ +static int compress16BitDepthTo8BitDepth(int value) { + //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1); + //byte compressedValue = (byte)(value * multiplier); + return value >> 8; +} +/** + * PNG supports four filtering types. These types are applied + * per row of image data. This method unfilters the given row + * based on the filterType. + */ +void filterRow(byte[] row, byte[] previousRow, int filterType) { + int byteOffset = headerChunk.getFilterByteOffset(); + switch (filterType) { + case PngIhdrChunk.FILTER_NONE: + break; + case PngIhdrChunk.FILTER_SUB: + for (int i = byteOffset; i < row.length; i++) { + int current = row[i] & 0xFF; + int left = row[i - byteOffset] & 0xFF; + row[i] = cast(byte)((current + left) & 0xFF); + } + break; + case PngIhdrChunk.FILTER_UP: + for (int i = 0; i < row.length; i++) { + int current = row[i] & 0xFF; + int above = previousRow[i] & 0xFF; + row[i] = cast(byte)((current + above) & 0xFF); + } + break; + case PngIhdrChunk.FILTER_AVERAGE: + for (int i = 0; i < row.length; i++) { + int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF; + int above = previousRow[i] & 0xFF; + int current = row[i] & 0xFF; + row[i] = cast(byte)((current + ((left + above) / 2)) & 0xFF); + } + break; + case PngIhdrChunk.FILTER_PAETH: + for (int i = 0; i < row.length; i++) { + int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF; + int aboveLeft = (i < byteOffset) ? 0 : previousRow[i - byteOffset] & 0xFF; + int above = previousRow[i] & 0xFF; + + int a = Math.abs(above - aboveLeft); + int b = Math.abs(left - aboveLeft); + int c = Math.abs(left - aboveLeft + above - aboveLeft); + + int preductor = 0; + if (a <= b && a <= c) { + preductor = left; + } else if (b <= c) { + preductor = above; + } else { + preductor = aboveLeft; + } + + int currentValue = row[i] & 0xFF; + row[i] = cast(byte) ((currentValue + preductor) & 0xFF); + } + break; + } +} + +} \ No newline at end of file diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngChunk.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngChunk.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,377 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngChunk; + + +import dwt.SWT; +import dwt.internal.image.LEDataInputStream; +import dwt.internal.image.PngFileReadState; +import dwt.internal.image.PngIhdrChunk; +import dwt.internal.image.PngPlteChunk; +import dwt.internal.image.PngIdatChunk; +import dwt.internal.image.PngIendChunk; +import dwt.internal.image.PngTrnsChunk; + +import tango.core.Exception; +import tango.text.convert.Format; + +class PngChunk { + byte[] reference; + + static const int LENGTH_OFFSET = 0; + static const int TYPE_OFFSET = 4; + static const int DATA_OFFSET = 8; + + static const int TYPE_FIELD_LENGTH = 4; + static const int LENGTH_FIELD_LENGTH = 4; + static const int MIN_LENGTH = 12; + + static const int CHUNK_UNKNOWN = -1; + // Critical chunks. + static const int CHUNK_IHDR = 0; + static const int CHUNK_PLTE = 1; + static const int CHUNK_IDAT = 2; + static const int CHUNK_IEND = 3; + // Non-critical chunks. + static const int CHUNK_tRNS = 5; + + static const byte[] TYPE_IHDR = cast(byte[])"IHDR";//{(byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R'}; + static const byte[] TYPE_PLTE = cast(byte[])"PLTE";//{(byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E'}; + static const byte[] TYPE_IDAT = cast(byte[])"IDAT";//{(byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T'}; + static const byte[] TYPE_IEND = cast(byte[])"IEND";//{(byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D'}; + static const byte[] TYPE_tRNS = cast(byte[])"tRNS";//{(byte) 't', (byte) 'R', (byte) 'N', (byte) 'S'}; + + static const int[] CRC_TABLE; + static this() { + CRC_TABLE = new int[256]; + for (int i = 0; i < 256; i++) { + CRC_TABLE[i] = i; + for (int j = 0; j < 8; j++) { + if ((CRC_TABLE[i] & 0x1) == 0) { + CRC_TABLE[i] = (CRC_TABLE[i] >> 1) & 0x7FFFFFFF; + } else { + CRC_TABLE[i] = 0xEDB88320 ^ ((CRC_TABLE[i] >> 1) & 0x7FFFFFFF); + } + } + } + } + + int length; + +/** + * Construct a PngChunk using the reference bytes + * given. + */ +this(byte[] reference) { + setReference(reference); + if (reference.length < LENGTH_OFFSET + LENGTH_FIELD_LENGTH) SWT.error(SWT.ERROR_INVALID_IMAGE); + length = getInt32(LENGTH_OFFSET); +} + +/** + * Construct a PngChunk with the specified number of + * data bytes. + */ +this(int dataLength) { + this(new byte[MIN_LENGTH + dataLength]); + setLength(dataLength); +} + +/** + * Get the PngChunk's reference byteArray; + */ +byte[] getReference() { + return reference; +} + +/** + * Set the PngChunk's reference byteArray; + */ +void setReference(byte[] reference) { + this.reference = reference; +} + +/** + * Get the 16-bit integer from the reference byte + * array at the given offset. + */ +int getInt16(int offset) { + int answer = 0; + answer |= (reference[offset] & 0xFF) << 8; + answer |= (reference[offset + 1] & 0xFF); + return answer; +} + +/** + * Set the 16-bit integer in the reference byte + * array at the given offset. + */ +void setInt16(int offset, int value) { + reference[offset] = cast(byte) ((value >> 8) & 0xFF); + reference[offset + 1] = cast(byte) (value & 0xFF); +} + +/** + * Get the 32-bit integer from the reference byte + * array at the given offset. + */ +int getInt32(int offset) { + int answer = 0; + answer |= (reference[offset] & 0xFF) << 24; + answer |= (reference[offset + 1] & 0xFF) << 16; + answer |= (reference[offset + 2] & 0xFF) << 8; + answer |= (reference[offset + 3] & 0xFF); + return answer; +} + +/** + * Set the 32-bit integer in the reference byte + * array at the given offset. + */ +void setInt32(int offset, int value) { + reference[offset] = cast(byte) ((value >> 24) & 0xFF); + reference[offset + 1] = cast(byte) ((value >> 16) & 0xFF); + reference[offset + 2] = cast(byte) ((value >> 8) & 0xFF); + reference[offset + 3] = cast(byte) (value & 0xFF); +} + +/** + * Get the length of the data component of this chunk. + * This is not the length of the entire chunk. + */ +int getLength() { + return length; +} + +/** + * Set the length of the data component of this chunk. + * This is not the length of the entire chunk. + */ +void setLength(int value) { + setInt32(LENGTH_OFFSET, value); + length = value; +} + +/** + * Get the chunk type. This is a four byte value. + * Each byte should be an ASCII character. + * The first byte is upper case if the chunk is critical. + * The second byte is upper case if the chunk is publicly defined. + * The third byte must be upper case. + * The fourth byte is upper case if the chunk is unsafe to copy. + * Public chunk types are defined by the PNG Development Group. + */ +byte[] getTypeBytes() { + return reference[ TYPE_OFFSET .. TYPE_OFFSET+TYPE_FIELD_LENGTH ].dup; +} + +/** + * Set the chunk type. This is a four byte value. + * Each byte should be an ASCII character. + * The first byte is upper case if the chunk is critical. + * The second byte is upper case if the chunk is publicly defined. + * The third byte must be upper case. + * The fourth byte is upper case if the chunk is unsafe to copy. + * Public chunk types are defined by the PNG Development Group. + */ +void setType(byte[] value) { + if (value.length != TYPE_FIELD_LENGTH) { + SWT.error (SWT.ERROR_INVALID_ARGUMENT); + } + reference[ TYPE_OFFSET .. TYPE_OFFSET+TYPE_FIELD_LENGTH ] = value[ 0 .. TYPE_FIELD_LENGTH ]; +} + +/** + * Get the chunk's data. + */ +byte[] getData() { + int dataLength = getLength(); + if (reference.length < MIN_LENGTH + dataLength) { + SWT.error (SWT.ERROR_INVALID_RANGE); + } + return reference[ DATA_OFFSET .. DATA_OFFSET+dataLength ].dup; +} + +/** + * Set the chunk's data. + * This method has two side-effects. + * 1. It will set the length field to be the length + * of the data array given. + * 2. It will set the CRC field to the computed CRC + * value of the data array given. + */ +void setData(byte[] data) { + setLength(data.length); + reference[ DATA_OFFSET .. DATA_OFFSET+data.length ] = data; + setCRC(computeCRC()); +} + +/** + * Get the CRC value for the chunk's data. + * Ensure that the length field has a good + * value before making this call. + */ +int getCRC() { + int crcOffset = DATA_OFFSET + getLength(); + return getInt32(crcOffset); +} + +/** + * Set the CRC value for the chunk's data. + * Ensure that the length field has a good + * value before making this call. + */ +void setCRC(int value) { + int crcOffset = DATA_OFFSET + getLength(); + setInt32(crcOffset, value); +} + +/** + * Get the chunk's total size including the length, type, and crc fields. + */ +int getSize() { + return MIN_LENGTH + getLength(); +} + +/** + * Compute the CRC value for the chunk's data. Answer + * whether this value matches the value stored in the + * chunk. + */ +bool checkCRC() { + int crc = computeCRC(); + int storedCRC = getCRC(); + return crc == storedCRC; +} + +/** + * Answer the CRC value of chunk's data. + */ +int computeCRC() { + int crc = 0xFFFFFFFF; + int start = TYPE_OFFSET; + int stop = DATA_OFFSET + getLength(); + for (int i = start; i < stop; i++) { + int index = (crc ^ reference[i]) & 0xFF; + crc = CRC_TABLE[index] ^ ((crc >> 8) & 0x00FFFFFF); + } + return ~crc; +} + +bool typeMatchesArray(byte[] array) { + for (int i = 0; i < TYPE_FIELD_LENGTH; i++) { + if (reference[TYPE_OFFSET + i] != array[i]){ + return false; + } + } + return true; +} + +bool isCritical() { + char c = cast(char) getTypeBytes()[0]; + return 'A' <= c && c <= 'Z'; +} + +int getChunkType() { + if (typeMatchesArray(TYPE_IHDR)) return CHUNK_IHDR; + if (typeMatchesArray(TYPE_PLTE)) return CHUNK_PLTE; + if (typeMatchesArray(TYPE_IDAT)) return CHUNK_IDAT; + if (typeMatchesArray(TYPE_IEND)) return CHUNK_IEND; + if (typeMatchesArray(TYPE_tRNS)) return CHUNK_tRNS; + return CHUNK_UNKNOWN; +} + +/** + * Read the next PNG chunk from the input stream given. + * If unable to read a chunk, return null. + */ +static PngChunk readNextFromStream(LEDataInputStream stream) { + try { + int headerLength = LENGTH_FIELD_LENGTH + TYPE_FIELD_LENGTH; + byte[] headerBytes = new byte[headerLength]; + int result = stream.read(headerBytes, 0, headerLength); + stream.unread(headerBytes); + if (result != headerLength) return null; + + PngChunk tempChunk = new PngChunk(headerBytes); + + int chunkLength = tempChunk.getSize(); + byte[] chunk = new byte[chunkLength]; + result = stream.read(chunk, 0, chunkLength); + if (result != chunkLength) return null; + + switch (tempChunk.getChunkType()) { + case CHUNK_IHDR: + return new PngIhdrChunk(chunk); + case CHUNK_PLTE: + return new PngPlteChunk(chunk); + case CHUNK_IDAT: + return new PngIdatChunk(chunk); + case CHUNK_IEND: + return new PngIendChunk(chunk); + case CHUNK_tRNS: + return new PngTrnsChunk(chunk); + default: + return new PngChunk(chunk); + } + } catch (IOException e) { + return null; + } +} + +/** + * Answer whether the chunk is a valid PNG chunk. + */ +void validate(PngFileReadState readState, PngIhdrChunk headerChunk) { + if (reference.length < MIN_LENGTH) SWT.error(SWT.ERROR_INVALID_IMAGE); + + byte[] type = getTypeBytes(); + + // The third character MUST be upper case. + char c = cast(char) type[2]; + if (!('A' <= c && c <= 'Z')) SWT.error(SWT.ERROR_INVALID_IMAGE); + + // All characters must be letters. + for (int i = 0; i < TYPE_FIELD_LENGTH; i++) { + c = cast(char) type[i]; + if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + } + + // The stored CRC must match the data's computed CRC. + if (!checkCRC()) SWT.error(SWT.ERROR_INVALID_IMAGE); +} + +/** + * Provided so that subclasses can override and add + * data to the toString() call. + */ +char[] contributeToString() { + return ""; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public char[] toString() { + char[] buffer = Format( "{\n\tLength: {}\n\tType: {}{}\n\tCRC: {:X}\n}", + getLength(), + cast(char[]) getTypeBytes(), + contributeToString(), + getCRC()); + return buffer; +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngChunkReader.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngChunkReader.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngChunkReader; + + +import dwt.SWT; +import dwt.internal.image.LEDataInputStream; +import dwt.internal.image.PngFileReadState; +import dwt.internal.image.PngIhdrChunk; +import dwt.internal.image.PngPlteChunk; +import dwt.internal.image.PngTrnsChunk; +import dwt.internal.image.PngChunk; + +public class PngChunkReader { + LEDataInputStream inputStream; + PngFileReadState readState; + PngIhdrChunk headerChunk; + PngPlteChunk paletteChunk; + +this(LEDataInputStream inputStream) { + this.inputStream = inputStream; + readState = new PngFileReadState(); + headerChunk = null; +} + +PngIhdrChunk getIhdrChunk() { + if (headerChunk is null) { + PngChunk chunk = PngChunk.readNextFromStream(inputStream); + if (chunk is null) SWT.error(SWT.ERROR_INVALID_IMAGE); + if(( headerChunk = cast(PngIhdrChunk) chunk ) !is null ){ + headerChunk.validate(readState, null); + } + else{ + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + } + return headerChunk; +} + +PngChunk readNextChunk() { + if (headerChunk is null) return getIhdrChunk(); + + PngChunk chunk = PngChunk.readNextFromStream(inputStream); + if (chunk is null) SWT.error(SWT.ERROR_INVALID_IMAGE); + switch (chunk.getChunkType()) { + case PngChunk.CHUNK_tRNS: + (cast(PngTrnsChunk) chunk).validate(readState, headerChunk, paletteChunk); + break; + case PngChunk.CHUNK_PLTE: + chunk.validate(readState, headerChunk); + paletteChunk = cast(PngPlteChunk) chunk; + break; + default: + chunk.validate(readState, headerChunk); + } + if (readState.readIDAT && !(chunk.getChunkType() == PngChunk.CHUNK_IDAT)) { + readState.readPixelData = true; + } + return chunk; +} + +bool readPixelData() { + return readState.readPixelData; +} + +bool hasMoreChunks() { + return !readState.readIEND; +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngDecodingDataStream.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngDecodingDataStream.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngDecodingDataStream; + + +import dwt.dwthelper.InputStream; + +import dwt.SWT; +import dwt.internal.image.PngLzBlockReader; + +public class PngDecodingDataStream : InputStream { + alias InputStream.read read; + InputStream stream; + byte currentByte; + int nextBitIndex; + + PngLzBlockReader lzBlockReader; + int adlerValue; + + static final int PRIME = 65521; + static final int MAX_BIT = 7; + +this(InputStream stream) { + super(); + this.stream = stream; + nextBitIndex = MAX_BIT + 1; + adlerValue = 1; + lzBlockReader = new PngLzBlockReader(this); + readCompressedDataHeader(); + lzBlockReader.readNextBlockHeader(); +} + +/** + * This method should be called when the image decoder thinks + * that all of the compressed image data has been read. This + * method will ensure that the next data value is an end of + * block marker. If there are more blocks after this one, + * the method will read them and ensure that they are empty. + */ +void assertImageDataAtEnd() { + lzBlockReader.assertCompressedDataAtEnd(); +} + +public void close() { + assertImageDataAtEnd(); + checkAdler(); +} + +int getNextIdatBits(int length) { + int value = 0; + for (int i = 0; i < length; i++) { + value |= (getNextIdatBit() << i); + } + return value; +} + +int getNextIdatBit() { + if (nextBitIndex > MAX_BIT) { + currentByte = getNextIdatByte(); + nextBitIndex = 0; + } + return (currentByte & (1 << nextBitIndex)) >> nextBitIndex++; +} + +byte getNextIdatByte() { + byte nextByte = cast(byte)stream.read(); + nextBitIndex = MAX_BIT + 1; + return nextByte; +} + +void updateAdler(byte value) { + int low = adlerValue & 0xFFFF; + int high = (adlerValue >> 16) & 0xFFFF; + int valueInt = value & 0xFF; + low = (low + valueInt) % PRIME; + high = (low + high) % PRIME; + adlerValue = (high << 16) | low; +} + +public override int read() { + byte nextDecodedByte = lzBlockReader.getNextByte(); + updateAdler(nextDecodedByte); + return nextDecodedByte & 0xFF; +} + +public override int read(byte[] buffer, int off, int len) { + for (int i = 0; i < len; i++) { + int b = read(); + if (b == -1) return i; + buffer[off + i] = cast(byte)b; + } + return len; +} + +void error() { + SWT.error(SWT.ERROR_INVALID_IMAGE); +} + +private void readCompressedDataHeader() { + byte headerByte1 = getNextIdatByte(); + byte headerByte2 = getNextIdatByte(); + + int number = ((headerByte1 & 0xFF) << 8) | (headerByte2 & 0xFF); + if (number % 31 != 0) error(); + + int compressionMethod = headerByte1 & 0x0F; + if (compressionMethod != 8) error(); + + int windowSizeHint = (headerByte1 & 0xF0) >> 4; + if (windowSizeHint > 7) error(); + int windowSize = (1 << (windowSizeHint + 8)); + lzBlockReader.setWindowSize(windowSize); + + int dictionary = (headerByte2 & (1 << 5)); + if (dictionary != 0) error(); + +// int compressionLevel = (headerByte2 & 0xC0) >> 6; +} + +void checkAdler() { + int storedAdler = ((getNextIdatByte() & 0xFF) << 24) + | ((getNextIdatByte() & 0xFF) << 16) + | ((getNextIdatByte() & 0xFF) << 8) + | (getNextIdatByte() & 0xFF); + if (storedAdler != adlerValue) error(); +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngDeflater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngDeflater.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,621 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngDeflater; + +import dwt.dwthelper.ByteArrayOutputStream; + +public class PngDeflater { + + static const int BASE = 65521; + static const int WINDOW = 32768; + static const int MIN_LENGTH = 3; + static const int MAX_MATCHES = 32; + static const int HASH = 8209; + + byte[] istr; + int inLength; + + ByteArrayOutputStream bytes; + + int adler32 = 1; + + int buffer, bitCount; + + Link[HASH] hashtable;// = new Link[HASH]; + Link[WINDOW] window;// = new Link[WINDOW]; + int nextWindow; + +public this(){ + bytes = new ByteArrayOutputStream(1024); +} + +class Link { + + int hash, value; + Link previous, next; + + this() { + + this.hash = 0; + this.value = 0; + this.previous = null; + this.next = null; + + } + +} + +class Match { + + int length, distance; + + this(int length, int distance) { + + this.length = length; + this.distance = distance; + + } + +} + +static const short mirrorBytes[] = [ cast(short) + + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, + +]; + +static class Code { + + int code, extraBits, min, max; + + this(int code, int extraBits, int min, int max) { + + this.code = code; + this.extraBits = extraBits; + this.min = min; + this.max = max; + + } + +} + +static const Code lengthCodes[]; +static const Code distanceCodes[]; + +static this() { + lengthCodes = [ + new Code(257, 0, 3, 3), + new Code(258, 0, 4, 4), + new Code(259, 0, 5, 5), + new Code(260, 0, 6, 6), + new Code(261, 0, 7, 7), + new Code(262, 0, 8, 8), + new Code(263, 0, 9, 9), + new Code(264, 0, 10, 10), + new Code(265, 1, 11, 12), + new Code(266, 1, 13, 14), + new Code(267, 1, 15, 16), + new Code(268, 1, 17, 18), + new Code(269, 2, 19, 22), + new Code(270, 2, 23, 26), + new Code(271, 2, 27, 30), + new Code(272, 2, 31, 34), + new Code(273, 3, 35, 42), + new Code(274, 3, 43, 50), + new Code(275, 3, 51, 58), + new Code(276, 3, 59, 66), + new Code(277, 4, 67, 82), + new Code(278, 4, 83, 98), + new Code(279, 4, 99, 114), + new Code(280, 4, 115, 130), + new Code(281, 5, 131, 162), + new Code(282, 5, 163, 194), + new Code(283, 5, 195, 226), + new Code(284, 5, 227, 257), + new Code(285, 0, 258, 258)]; + + distanceCodes = [ + new Code(0, 0, 1, 1), + new Code(1, 0, 2, 2), + new Code(2, 0, 3, 3), + new Code(3, 0, 4, 4), + new Code(4, 1, 5, 6), + new Code(5, 1, 7, 8), + new Code(6, 2, 9, 12), + new Code(7, 2, 13, 16), + new Code(8, 3, 17, 24), + new Code(9, 3, 25, 32), + new Code(10, 4, 33, 48), + new Code(11, 4, 49, 64), + new Code(12, 5, 65, 96), + new Code(13, 5, 97, 128), + new Code(14, 6, 129, 192), + new Code(15, 6, 193, 256), + new Code(16, 7, 257, 384), + new Code(17, 7, 385, 512), + new Code(18, 8, 513, 768), + new Code(19, 8, 769, 1024), + new Code(20, 9, 1025, 1536), + new Code(21, 9, 1537, 2048), + new Code(22, 10, 2049, 3072), + new Code(23, 10, 3073, 4096), + new Code(24, 11, 4097, 6144), + new Code(25, 11, 6145, 8192), + new Code(26, 12, 8193, 12288), + new Code(27, 12, 12289, 16384), + new Code(28, 13, 16385, 24576), + new Code(29, 13, 24577, 32768)]; +} + +void writeShortLSB(ByteArrayOutputStream baos, int theShort) { + + byte byte1 = cast(byte) (theShort & 0xff); + byte byte2 = cast(byte) ((theShort >> 8) & 0xff); + byte[] temp = [byte1, byte2]; + baos.write(temp, 0, 2); + +} + +void writeInt(ByteArrayOutputStream baos, int theInt) { + + byte byte1 = cast(byte) ((theInt >> 24) & 0xff); + byte byte2 = cast(byte) ((theInt >> 16) & 0xff); + byte byte3 = cast(byte) ((theInt >> 8) & 0xff); + byte byte4 = cast(byte) (theInt & 0xff); + byte[] temp = [byte1, byte2, byte3, byte4]; + baos.write(temp, 0, 4); + +} + +void updateAdler(byte value) { + + int low = adler32 & 0xffff; + int high = (adler32 >> 16) & 0xffff; + int valueInt = value & 0xff; + low = (low + valueInt) % BASE; + high = (low + high) % BASE; + adler32 = (high << 16) | low; + +} + +int hash(byte[] bytes) { + + int hash = ((bytes[0] & 0xff) << 24 | (bytes[1] & 0xff) << 16 | (bytes[2] & 0xff) << 8) % HASH; + if (hash < 0) { + hash = hash + HASH; + } + return hash; + +} + +void writeBits(int value, int count) { + + buffer |= value << bitCount; + bitCount += count; + if (bitCount >= 16) { + bytes.write(cast(byte) buffer); + bytes.write(cast(byte) (buffer >>> 8)); + buffer >>>= 16; + bitCount -= 16; + } + +} + +void alignToByte() { + + if (bitCount > 0) { + bytes.write(cast(byte) buffer); + if (bitCount > 8) bytes.write(cast(byte) (buffer >>> 8)); + } + buffer = 0; + bitCount = 0; + +} + +void outputLiteral(byte literal) { + + int i = literal & 0xff; + + if (i <= 143) { + // 0 through 143 are 8 bits long starting at 00110000 + writeBits(mirrorBytes[0x30 + i], 8); + } + else { + // 144 through 255 are 9 bits long starting at 110010000 + writeBits(1 + 2 * mirrorBytes[0x90 - 144 + i], 9); + } + +} + +Code findCode(int value, Code[] codes) { + + int i, j, k; + + i = -1; + j = codes.length; + while (true) { + k = (j + i) / 2; + if (value < codes[k].min) { + j = k; + } + else if (value > codes[k].max) { + i = k; + } + else { + return codes[k]; + } + } + +} + +void outputMatch(int length, int distance) { + + Code d, l; + int thisLength; + + while (length > 0) { + + // we can transmit matches of lengths 3 through 258 inclusive + // so if length exceeds 258, we must transmit in several steps, + // with 258 or less in each step + + if (length > 260) { + thisLength = 258; + } + else if (length <= 258) { + thisLength = length; + } + else { + thisLength = length - 3; + } + + length = length - thisLength; + + // find length code + l = findCode(thisLength, lengthCodes); + + // transmit the length code + // 256 through 279 are 7 bits long starting at 0000000 + // 280 through 287 are 8 bits long starting at 11000000 + if (l.code <= 279) { + writeBits(mirrorBytes[(l.code - 256) * 2], 7); + } + else { + writeBits(mirrorBytes[0xc0 - 280 + l.code], 8); + } + + // transmit the extra bits + if (l.extraBits != 0) { + writeBits(thisLength - l.min, l.extraBits); + } + + // find distance code + d = findCode(distance, distanceCodes); + + // transmit the distance code + // 5 bits long starting at 00000 + writeBits(mirrorBytes[d.code * 8], 5); + + // transmit the extra bits + if (d.extraBits != 0) { + writeBits(distance - d.min, d.extraBits); + } + + } + +} + +Match findLongestMatch(int position, Link firstPosition) { + + Link link = firstPosition; + int numberOfMatches = 0; + Match bestMatch = new Match(-1, -1); + + while (true) { + + int matchPosition = link.value; + + if (position - matchPosition < WINDOW && matchPosition != 0) { + + int i; + + for (i = 1; position + i < inLength; i++) { + if (istr[position + i] != istr[matchPosition + i]) { + break; + } + } + + if (i >= MIN_LENGTH) { + + if (i > bestMatch.length) { + bestMatch.length = i; + bestMatch.distance = position - matchPosition; + } + + numberOfMatches = numberOfMatches + 1; + + if (numberOfMatches == MAX_MATCHES) { + break; + } + + } + + } + + link = link.next; + if (link == null) { + break; + } + + } + + if (bestMatch.length < MIN_LENGTH || bestMatch.distance < 1 || bestMatch.distance > WINDOW) { + return null; + } + + return bestMatch; + +} + +void updateHashtable(int to, int from) { + + byte[] data = new byte[3]; + int hashval; + Link temp; + + for (int i = to; i < from; i++) { + + if (i + MIN_LENGTH > inLength) { + break; + } + + data[0] = istr[i]; + data[1] = istr[i + 1]; + data[2] = istr[i + 2]; + + hashval = hash(data); + + if (window[nextWindow].previous != null) { + window[nextWindow].previous.next = null; + } + else if (window[nextWindow].hash != 0) { + hashtable[window[nextWindow].hash].next = null; + } + + window[nextWindow].hash = hashval; + window[nextWindow].value = i; + window[nextWindow].previous = null; + temp = window[nextWindow].next = hashtable[hashval].next; + hashtable[hashval].next = window[nextWindow]; + if (temp != null) { + temp.previous = window[nextWindow]; + } + + nextWindow = nextWindow + 1; + if (nextWindow == WINDOW) { + nextWindow = 0; + } + + } + +} + +void compress() { + + int position, newPosition; + byte[] data = new byte[3]; + int hashval; + for (int i = 0; i < HASH; i++) { + hashtable[i] = new Link(); + } + for (int i = 0; i < WINDOW; i++) { + window[i] = new Link(); + } + nextWindow = 0; + Link firstPosition; + Match match; + int deferredPosition = -1; + Match deferredMatch = null; + + writeBits(0x01, 1); // BFINAL = 0x01 (final block) + writeBits(0x01, 2); // BTYPE = 0x01 (compression with fixed Huffman codes) + + // just output first byte so we never match at zero + outputLiteral(istr[0]); + position = 1; + + while (position < inLength) { + + if (inLength - position < MIN_LENGTH) { + outputLiteral(istr[position]); + position = position + 1; + continue; + } + + data[0] = istr[position]; + data[1] = istr[position + 1]; + data[2] = istr[position + 2]; + + hashval = hash(data); + firstPosition = hashtable[hashval]; + + match = findLongestMatch(position, firstPosition); + + updateHashtable(position, position + 1); + + if (match != null) { + + if (deferredMatch != null) { + if (match.length > deferredMatch.length + 1) { + // output literal at deferredPosition + outputLiteral(istr[deferredPosition]); + // defer this match + deferredPosition = position; + deferredMatch = match; + position = position + 1; + } + else { + // output deferredMatch + outputMatch(deferredMatch.length, deferredMatch.distance); + newPosition = deferredPosition + deferredMatch.length; + deferredPosition = -1; + deferredMatch = null; + updateHashtable(position + 1, newPosition); + position = newPosition; + } + } + else { + // defer this match + deferredPosition = position; + deferredMatch = match; + position = position + 1; + } + + } + + else { + + // no match found + if (deferredMatch != null) { + outputMatch(deferredMatch.length, deferredMatch.distance); + newPosition = deferredPosition + deferredMatch.length; + deferredPosition = -1; + deferredMatch = null; + updateHashtable(position + 1, newPosition); + position = newPosition; + } + else { + outputLiteral(istr[position]); + position = position + 1; + } + + } + + } + + writeBits(0, 7); // end of block code + alignToByte(); + +} + +void compressHuffmanOnly() { + + int position; + + writeBits(0x01, 1); // BFINAL = 0x01 (final block) + writeBits(0x01, 2); // BTYPE = 0x01 (compression with fixed Huffman codes) + + for (position = 0; position < inLength;) { + + outputLiteral(istr[position]); + position = position + 1; + + } + + writeBits(0, 7); // end of block code + alignToByte(); + +} + +void store() { + + // stored blocks are limited to 0xffff bytes + + int start = 0; + int length = inLength; + int blockLength; + int BFINAL = 0x00; // BFINAL = 0x00 or 0x01 (if final block), BTYPE = 0x00 (no compression) + + while (length > 0) { + + if (length < 65535) { + blockLength = length; + BFINAL = 0x01; + } + else { + blockLength = 65535; + BFINAL = 0x00; + } + + // write data header + bytes.write(cast(byte) BFINAL); + writeShortLSB(bytes, blockLength); // LEN + writeShortLSB(bytes, blockLength ^ 0xffff); // NLEN (one's complement of LEN) + + // write actual data + bytes.write(istr, start, blockLength); + + length = length - blockLength; + start = start + blockLength; + + } + +} + +public byte[] deflate(byte[] input) { + + istr = input; + inLength = input.length; + + // write zlib header + bytes.write(cast(byte) 0x78); // window size = 0x70 (32768), compression method = 0x08 + bytes.write(cast(byte) 0x9C); // compression level = 0x80 (default), check bits = 0x1C + + // compute checksum + for (int i = 0; i < inLength; i++) { + updateAdler(istr[i]); + } + + //store(); + + //compressHuffmanOnly(); + + compress(); + + // write checksum + writeInt(bytes, adler32); + + return bytes.toByteArray(); + +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngEncoder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngEncoder.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,356 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngEncoder; + +import dwt.internal.image.LEDataOutputStream; +import dwt.internal.image.PngDeflater; +import dwt.dwthelper.ByteArrayOutputStream; +import dwt.SWT; +import dwt.graphics.ImageData; +import dwt.graphics.ImageLoader; +import dwt.graphics.RGB; +import dwt.internal.image.PngChunk; + +import tango.core.Exception; + +final class PngEncoder { + + static const byte SIGNATURE[] = [cast(byte) '\211', cast(byte) 'P', cast(byte) 'N', cast(byte) 'G', cast(byte) '\r', cast(byte) '\n', cast(byte) '\032', cast(byte) '\n']; + static const byte TAG_IHDR[] = [cast(byte) 'I', cast(byte) 'H', cast(byte) 'D', cast(byte) 'R']; + static const byte TAG_PLTE[] = [cast(byte) 'P', cast(byte) 'L', cast(byte) 'T', cast(byte) 'E']; + static const byte TAG_TRNS[] = [cast(byte) 't', cast(byte) 'R', cast(byte) 'N', cast(byte) 'S']; + static const byte TAG_IDAT[] = [cast(byte) 'I', cast(byte) 'D', cast(byte) 'A', cast(byte) 'T']; + static const byte TAG_IEND[] = [cast(byte) 'I', cast(byte) 'E', cast(byte) 'N', cast(byte) 'D']; + + ByteArrayOutputStream bytes; + PngChunk chunk; + + ImageLoader loader; + ImageData data; + int transparencyType; + + int width, height, bitDepth, colorType; + + int compressionMethod = 0; + int filterMethod = 0; + int interlaceMethod = 0; + +public this(ImageLoader loader) { + this.bytes = new ByteArrayOutputStream(1024); + this.loader = loader; + this.data = loader.data[0]; + this.transparencyType = data.getTransparencyType(); + + this.width = data.width; + this.height = data.height; + + this.bitDepth = 8; + + this.colorType = 2; + + if (data.palette.isDirect) { + if (transparencyType == SWT.TRANSPARENCY_ALPHA) { + this.colorType = 6; + } + } + else { + this.colorType = 3; + } + + if (!(colorType == 2 || colorType == 3 || colorType == 6)) SWT.error(SWT.ERROR_INVALID_IMAGE); + +} + +void writeShort(ByteArrayOutputStream baos, int theShort) { + + byte byte1 = cast(byte) ((theShort >> 8) & 0xff); + byte byte2 = cast(byte) (theShort & 0xff); + byte[] temp = [byte1, byte2]; + baos.write(temp, 0, 2); + +} + +void writeInt(ByteArrayOutputStream baos, int theInt) { + + byte byte1 = cast(byte) ((theInt >> 24) & 0xff); + byte byte2 = cast(byte) ((theInt >> 16) & 0xff); + byte byte3 = cast(byte) ((theInt >> 8) & 0xff); + byte byte4 = cast(byte) (theInt & 0xff); + byte[] temp = [byte1, byte2, byte3, byte4]; + baos.write(temp, 0, 4); + +} + +void writeChunk(byte[] tag, byte[] buffer) { + + int bufferLength = (buffer != null) ? buffer.length : 0; + + chunk = new PngChunk(bufferLength); + + writeInt(bytes, bufferLength); + bytes.write(tag, 0, 4); + chunk.setType(tag); + if (bufferLength != 0) { + bytes.write(buffer, 0, bufferLength); + chunk.setData(buffer); + } + else { + chunk.setCRC(chunk.computeCRC()); + } + writeInt(bytes, chunk.getCRC()); + +} + +void writeSignature() { + + bytes.write(SIGNATURE, 0, 8); + +} + +void writeHeader() { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(13); + + writeInt(baos, width); + writeInt(baos, height); + baos.write(bitDepth); + baos.write(colorType); + baos.write(compressionMethod); + baos.write(filterMethod); + baos.write(interlaceMethod); + + writeChunk(TAG_IHDR, baos.toByteArray()); + +} + +void writePalette() { + + RGB[] RGBs = data.palette.getRGBs(); + + if (RGBs.length > 256) SWT.error(SWT.ERROR_INVALID_IMAGE); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(RGBs.length); + + for (int i = 0; i < RGBs.length; i++) { + + baos.write(cast(byte) RGBs[i].red); + baos.write(cast(byte) RGBs[i].green); + baos.write(cast(byte) RGBs[i].blue); + + } + + writeChunk(TAG_PLTE, baos.toByteArray()); + +} + +void writeTransparency() { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + switch (transparencyType) { + + case SWT.TRANSPARENCY_ALPHA: + + int pixelValue, alphaValue; + + byte[] alphas = new byte[data.palette.getRGBs().length]; + + for (int y = 0; y < height; y++) { + + for (int x = 0; x < width; x++) { + + pixelValue = data.getPixel(x, y); + alphaValue = data.getAlpha(x, y); + + alphas[pixelValue] = cast(byte) alphaValue; + + } + + } + + baos.write(alphas, 0, alphas.length); + + break; + + case SWT.TRANSPARENCY_PIXEL: + + int pixel = data.transparentPixel; + + if (colorType == 2) { + + int redMask = data.palette.redMask; + int redShift = data.palette.redShift; + int greenMask = data.palette.greenMask; + int greenShift = data.palette.greenShift; + int blueShift = data.palette.blueShift; + int blueMask = data.palette.blueMask; + + int r = pixel & redMask; + r = (redShift < 0) ? r >>> -redShift : r << redShift; + int g = pixel & greenMask; + g = (greenShift < 0) ? g >>> -greenShift : g << greenShift; + int b = pixel & blueMask; + b = (blueShift < 0) ? b >>> -blueShift : b << blueShift; + + writeShort(baos, r); + writeShort(baos, g); + writeShort(baos, b); + + } + + if (colorType == 3) { + + byte[] padding = new byte[pixel + 1]; + + for (int i = 0; i < pixel; i++) { + + padding[i] = cast(byte) 255; + + } + + padding[pixel] = cast(byte) 0; + + baos.write(padding, 0, padding.length); + + } + + break; + + } + + writeChunk(TAG_TRNS, baos.toByteArray()); + +} + +void writeImageData() { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); + + if (colorType == 3) { + + int[] lineData = new int[width]; + + for (int y = 0; y < height; y++) { + + byte filter[] = [0]; + baos.write(filter, 0, 1); + + data.getPixels(0, y, width, lineData, 0); + + for (int x = 0; x < lineData.length; x++) { + + baos.write(cast(byte) lineData[x]); + + } + + } + + } + + else { + + int[] lineData = new int[width]; + byte[] alphaData = new byte[width]; + + int redMask = data.palette.redMask; + int redShift = data.palette.redShift; + int greenMask = data.palette.greenMask; + int greenShift = data.palette.greenShift; + int blueShift = data.palette.blueShift; + int blueMask = data.palette.blueMask; + + for (int y = 0; y < height; y++) { + + byte filter[] = [0]; + baos.write(filter, 0, 1); + + data.getPixels(0, y, width, lineData, 0); + + if (colorType == 6) { + data.getAlphas(0, y, width, alphaData, 0); + } + + for (int x = 0; x < lineData.length; x++) { + + int pixel = lineData[x]; + + int r = pixel & redMask; + r = (redShift < 0) ? r >>> -redShift : r << redShift; + int g = pixel & greenMask; + g = (greenShift < 0) ? g >>> -greenShift : g << greenShift; + int b = pixel & blueMask; + b = (blueShift < 0) ? b >>> -blueShift : b << blueShift; + + byte pixels[] = [cast(byte) r, cast(byte) g, cast(byte) b]; + baos.write(pixels, 0, 3); + + if (colorType == 6) { + + byte alpha[] = [alphaData[x]]; + baos.write(alpha, 0, 1); + + } + + } + + } + + } + + PngDeflater deflater = new PngDeflater(); + byte[] compressed = deflater.deflate(baos.toByteArray()); + + writeChunk(TAG_IDAT, compressed); + +} + +void writeEnd() { + + writeChunk(TAG_IEND, null); + +} + +public void encode(LEDataOutputStream outputStream) { + + try { + + writeSignature(); + writeHeader(); + + if (colorType == 3) { + writePalette(); + } + + bool transparencyAlpha = (transparencyType == SWT.TRANSPARENCY_ALPHA); + bool transparencyPixel = (transparencyType == SWT.TRANSPARENCY_PIXEL); + bool type2Transparency = (colorType == 2 && transparencyPixel); + bool type3Transparency = (colorType == 3 && (transparencyAlpha || transparencyPixel)); + + if (type2Transparency || type3Transparency) { + writeTransparency(); + } + + writeImageData(); + writeEnd(); + + outputStream.write(bytes.toByteArray()); + + } + + catch (IOException e) { + + SWT.error(SWT.ERROR_IO, e); + + } + +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngFileReadState.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngFileReadState.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 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.PngFileReadState; + + +class PngFileReadState { + bool readIHDR; + bool readPLTE; + bool readIDAT; + bool readIEND; + + // Non - critical chunks + bool readTRNS; + + // Set to true after IDATs have been read. + bool readPixelData; +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngHuffmanTable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngHuffmanTable.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngHuffmanTable; + +import dwt.internal.image.PngDecodingDataStream; + +public class PngHuffmanTable { + CodeLengthInfo[] codeLengthInfo; + int[] codeValues; + + static const int MAX_CODE_LENGTH = 15; + static const int BAD_CODE = 0xFFFFFFF; + static const int incs[] = [1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1]; + +this (int[] lengths) { + initialize(lengths); + generateTable(lengths); +} + +private void initialize(int[] lengths) { + codeValues = new int[lengths.length]; + for (int i = 0; i < codeValues.length; i++) { + codeValues[i] = i; + } + + // minCodesByLength[n] : The smallest Huffman code of length n + 1. + // maxCodesByLength[n] : The largest Huffman code of length n + 1. + // indexesByLength[n] : Index into the values array. First value with a code of length n + 1. + codeLengthInfo = new CodeLengthInfo[MAX_CODE_LENGTH]; + for (int i = 0; i < MAX_CODE_LENGTH; i++) { + codeLengthInfo[i] = new CodeLengthInfo(); + codeLengthInfo[i].length = i; + codeLengthInfo[i].baseIndex = 0; + codeLengthInfo[i].min = BAD_CODE; + codeLengthInfo[i].max = -1; + } +} + +private void generateTable(int[] lengths) { + // Sort the values using shellsort. Primary key is code size. Secondary key is value. + int codeValuesTemp; + for (int k = 0; k < 16; k++) { + for (int h = incs[k], i = h; i < lengths.length; i++) { + int v = lengths[i]; + codeValuesTemp = codeValues[i]; + int j = i; + while (j >= h && (lengths[j - h] > v || (lengths[j - h] == v && codeValues[j - h] > codeValuesTemp))) { + lengths[j] = lengths[j - h]; + codeValues[j] = codeValues[j - h]; + j -= h; + } + lengths[j] = v; + codeValues[j] = codeValuesTemp; + } + } + + // These values in these arrays correspond to the elements of the + // "values" array. The Huffman code for codeValues[N] is codes[N] + // and the length of the code is lengths[N]. + int[] codes = new int[lengths.length]; + int lastLength = 0; + int code = 0; + for (int i = 0; i < lengths.length; i++) { + while (lastLength != lengths[i]) { + lastLength++; + code <<= 1; + } + if (lastLength != 0) { + codes[i] = code; + code++; + } + } + + int last = 0; + for (int i = 0; i < lengths.length; i++) { + if (last != lengths[i]) { + last = lengths[i]; + codeLengthInfo[last - 1].baseIndex = i; + codeLengthInfo[last - 1].min = codes[i]; + } + if (last != 0) codeLengthInfo[last - 1].max = codes[i]; + } +} + +int getNextValue(PngDecodingDataStream stream) { + int code = stream.getNextIdatBit(); + int codelength = 0; + + // Here we are taking advantage of the fact that 1 bits are used as + // a prefix to the longer codeValues. + while (codelength < MAX_CODE_LENGTH && code > codeLengthInfo[codelength].max) { + code = ((code << 1) | stream.getNextIdatBit()); + codelength++; + } + if (codelength >= MAX_CODE_LENGTH) stream.error(); + + // Now we have a Huffman code of length (codelength + 1) that + // is somewhere in the range + // minCodesByLength[codelength]..maxCodesByLength[codelength]. + // This code is the (offset + 1)'th code of (codelength + 1); + int offset = code - codeLengthInfo[codelength].min; + + // indexesByLength[codelength] is the first code of length (codelength + 1) + // so now we can look up the value for the Huffman code in the table. + int index = codeLengthInfo[codelength].baseIndex + offset; + return codeValues[index]; +} + +class CodeLengthInfo { + int length; + int max; + int min; + int baseIndex; +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngHuffmanTables.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngHuffmanTables.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngHuffmanTables; + +import dwt.internal.image.PngHuffmanTable; +import dwt.internal.image.PngDecodingDataStream; +import dwt.internal.image.PngLzBlockReader; + +public class PngHuffmanTables { + PngHuffmanTable literalTable; + PngHuffmanTable distanceTable; + + static PngHuffmanTable FixedLiteralTable; + static PngHuffmanTable FixedDistanceTable; + + static final int LiteralTableSize = 288; + static final int[] FixedLiteralLengths = [ + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, + ]; + + static final int DistanceTableSize = 32; + static final int[] FixedDistanceLengths = [ + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + ]; + + static final int LengthCodeTableSize = 19; + static final int[] LengthCodeOrder = [ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15 + ]; + +static PngHuffmanTables getDynamicTables(PngDecodingDataStream stream) { + return new PngHuffmanTables(stream); +} +static PngHuffmanTables getFixedTables() { + return new PngHuffmanTables(); +} + +private PngHuffmanTable getFixedLiteralTable() { + if (FixedLiteralTable == null) { + FixedLiteralTable = new PngHuffmanTable(FixedLiteralLengths); + } + return FixedLiteralTable; +} + +private PngHuffmanTable getFixedDistanceTable() { + if (FixedDistanceTable == null) { + FixedDistanceTable = new PngHuffmanTable(FixedDistanceLengths); + } + return FixedDistanceTable; +} + +private this () { + literalTable = getFixedLiteralTable(); + distanceTable = getFixedDistanceTable(); +} + +private this (PngDecodingDataStream stream) { + int literals = PngLzBlockReader.FIRST_LENGTH_CODE + + stream.getNextIdatBits(5); + int distances = PngLzBlockReader.FIRST_DISTANCE_CODE + + stream.getNextIdatBits(5); + int codeLengthCodes = PngLzBlockReader.FIRST_CODE_LENGTH_CODE + + stream.getNextIdatBits(4); + + if (codeLengthCodes > PngLzBlockReader.LAST_CODE_LENGTH_CODE) { + stream.error(); + } + + /* Tricky, tricky, tricky. The length codes are stored in + * a very odd order. (For the order, see the definition of + * the static field lengthCodeOrder.) Also, the data may + * not contain values for all the codes. It may just contain + * values for the first X number of codes. The table should + * be of size regardless of the number + * of values actually given in the table. + */ + int[] lengthCodes = new int[LengthCodeTableSize]; + for (int i = 0; i < codeLengthCodes; i++) { + lengthCodes[LengthCodeOrder[i]] = stream.getNextIdatBits(3); + } + PngHuffmanTable codeLengthsTable = new PngHuffmanTable(lengthCodes); + + int[] literalLengths = readLengths( + stream, literals, codeLengthsTable, LiteralTableSize); + int[] distanceLengths = readLengths( + stream, distances, codeLengthsTable, DistanceTableSize); + + literalTable = new PngHuffmanTable(literalLengths); + distanceTable = new PngHuffmanTable(distanceLengths); +} + +private int [] readLengths (PngDecodingDataStream stream, + int numLengths, + PngHuffmanTable lengthsTable, + int tableSize) +{ + int[] lengths = new int[tableSize]; + + for (int index = 0; index < numLengths;) { + int value = lengthsTable.getNextValue(stream); + if (value < 16) { + // Literal value + lengths[index] = value; + index++; + } else if (value == 16) { + // Repeat the previous code 3-6 times. + int count = stream.getNextIdatBits(2) + 3; + for (int i = 0; i < count; i++) { + lengths[index] = lengths [index - 1]; + index++; + } + } else if (value == 17) { + // Repeat 0 3-10 times. + int count = stream.getNextIdatBits(3) + 3; + for (int i = 0; i < count; i++) { + lengths[index] = 0; + index++; + } + } else if (value == 18) { + // Repeat 0 11-138 times. + int count = stream.getNextIdatBits(7) + 11; + for (int i = 0; i < count; i++) { + lengths[index] = 0; + index++; + } + } else { + stream.error(); + } + } + return lengths; +} + +int getNextLiteralValue(PngDecodingDataStream stream) { + return literalTable.getNextValue(stream); +} + +int getNextDistanceValue(PngDecodingDataStream stream) { + return distanceTable.getNextValue(stream); +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngIdatChunk.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngIdatChunk.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngIdatChunk; + + +import dwt.SWT; +import dwt.internal.image.PngFileReadState; +import dwt.internal.image.PngIhdrChunk; +import dwt.internal.image.PngChunk; + +class PngIdatChunk : PngChunk { + + static const int HEADER_BYTES_LENGTH = 2; + static const int ADLER_FIELD_LENGTH = 4; + static const int HEADER_BYTE1_DATA_OFFSET = DATA_OFFSET + 0; + static const int HEADER_BYTE2_DATA_OFFSET = DATA_OFFSET + 1; + static const int ADLER_DATA_OFFSET = DATA_OFFSET + 2; // plus variable compressed data length + +this(byte headerByte1, byte headerByte2, byte[] data, int adler) { + super(data.length + HEADER_BYTES_LENGTH + ADLER_FIELD_LENGTH); + setType(TYPE_IDAT); + reference[HEADER_BYTE1_DATA_OFFSET] = headerByte1; + reference[HEADER_BYTE2_DATA_OFFSET] = headerByte2; + reference[ DATA_OFFSET .. DATA_OFFSET+data.length ] = data; + setInt32(ADLER_DATA_OFFSET, adler); + setCRC(computeCRC()); +} + +this(byte[] reference) { + super(reference); +} + +int getChunkType() { + return CHUNK_IDAT; +} + +/** + * Answer whether the chunk is a valid IDAT chunk. + */ +void validate(PngFileReadState readState, PngIhdrChunk headerChunk) { + if (!readState.readIHDR + || (headerChunk.getMustHavePalette() && !readState.readPLTE) + || readState.readIEND) + { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } else { + readState.readIDAT = true; + } + + super.validate(readState, headerChunk); +} + +byte getDataByteAtOffset(int offset) { + return reference[DATA_OFFSET + offset]; +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngIendChunk.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngIendChunk.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngIendChunk; + + +import dwt.SWT; +import dwt.internal.image.PngFileReadState; +import dwt.internal.image.PngIhdrChunk; +import dwt.internal.image.PngChunk; + +class PngIendChunk : PngChunk { + +this() { + super(0); + setType(TYPE_IEND); + setCRC(computeCRC()); +} + +this(byte[] reference){ + super(reference); +} + +int getChunkType() { + return CHUNK_IEND; +} + +/** + * Answer whether the chunk is a valid IEND chunk. + */ +void validate(PngFileReadState readState, PngIhdrChunk headerChunk) { + // An IEND chunk is invalid if no IHDR has been read. + // Or if a palette is required and has not been read. + // Or if no IDAT chunk has been read. + if (!readState.readIHDR + || (headerChunk.getMustHavePalette() && !readState.readPLTE) + || !readState.readIDAT + || readState.readIEND) + { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } else { + readState.readIEND = true; + } + + super.validate(readState, headerChunk); + + // IEND chunks are not allowed to have any data. + if (getLength() > 0) SWT.error(SWT.ERROR_INVALID_IMAGE); +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngIhdrChunk.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngIhdrChunk.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,401 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngIhdrChunk; + + +import dwt.SWT; +import dwt.graphics.PaletteData; +import dwt.graphics.RGB; +import dwt.internal.image.PngFileReadState; +import dwt.internal.image.PngIhdrChunk; +import dwt.internal.image.PngChunk; +import tango.text.convert.Format; + +class PngIhdrChunk : PngChunk { + static const int IHDR_DATA_LENGTH = 13; + + static const int WIDTH_DATA_OFFSET = DATA_OFFSET + 0; + static const int HEIGHT_DATA_OFFSET = DATA_OFFSET + 4; + static const int BIT_DEPTH_OFFSET = DATA_OFFSET + 8; + static const int COLOR_TYPE_OFFSET = DATA_OFFSET + 9; + static const int COMPRESSION_METHOD_OFFSET = DATA_OFFSET + 10; + static const int FILTER_METHOD_OFFSET = DATA_OFFSET + 11; + static const int INTERLACE_METHOD_OFFSET = DATA_OFFSET + 12; + + static const byte COLOR_TYPE_GRAYSCALE = 0; + static const byte COLOR_TYPE_RGB = 2; + static const byte COLOR_TYPE_PALETTE = 3; + static const byte COLOR_TYPE_GRAYSCALE_WITH_ALPHA = 4; + static const byte COLOR_TYPE_RGB_WITH_ALPHA = 6; + + static const int INTERLACE_METHOD_NONE = 0; + static const int INTERLACE_METHOD_ADAM7 = 1; + + static const int FILTER_NONE = 0; + static const int FILTER_SUB = 1; + static const int FILTER_UP = 2; + static const int FILTER_AVERAGE = 3; + static const int FILTER_PAETH = 4; + + static const byte[] ValidBitDepths = [ cast(byte)1, 2, 4, 8, 16]; + static const byte[] ValidColorTypes = [ cast(byte)0, 2, 3, 4, 6]; + + int width, height; + byte bitDepth, colorType, compressionMethod, filterMethod, interlaceMethod; + +this(int width, int height, byte bitDepth, byte colorType, byte compressionMethod, byte filterMethod, byte interlaceMethod) { + super(IHDR_DATA_LENGTH); + setType(TYPE_IHDR); + setWidth(width); + setHeight(height); + setBitDepth(bitDepth); + setColorType(colorType); + setCompressionMethod(compressionMethod); + setFilterMethod(filterMethod); + setInterlaceMethod(interlaceMethod); + setCRC(computeCRC()); +} + +/** + * Construct a PNGChunk using the reference bytes + * given. + */ +this(byte[] reference) { + super(reference); + if (reference.length <= IHDR_DATA_LENGTH) SWT.error(SWT.ERROR_INVALID_IMAGE); + width = getInt32(WIDTH_DATA_OFFSET); + height = getInt32(HEIGHT_DATA_OFFSET); + bitDepth = reference[BIT_DEPTH_OFFSET]; + colorType = reference[COLOR_TYPE_OFFSET]; + compressionMethod = reference[COMPRESSION_METHOD_OFFSET]; + filterMethod = reference[FILTER_METHOD_OFFSET]; + interlaceMethod = reference[INTERLACE_METHOD_OFFSET]; +} + +int getChunkType() { + return CHUNK_IHDR; +} + +/** + * Get the image's width in pixels. + */ +int getWidth() { + return width; +} + +/** + * Set the image's width in pixels. + */ +void setWidth(int value) { + setInt32(WIDTH_DATA_OFFSET, value); + width = value; +} + +/** + * Get the image's height in pixels. + */ +int getHeight() { + return height; +} + +/** + * Set the image's height in pixels. + */ +void setHeight(int value) { + setInt32(HEIGHT_DATA_OFFSET, value); + height = value; +} + +/** + * Get the image's bit depth. + * This is limited to the values 1, 2, 4, 8, or 16. + */ +byte getBitDepth() { + return bitDepth; +} + +/** + * Set the image's bit depth. + * This is limited to the values 1, 2, 4, 8, or 16. + */ +void setBitDepth(byte value) { + reference[BIT_DEPTH_OFFSET] = value; + bitDepth = value; +} + +/** + * Get the image's color type. + * This is limited to the values: + * 0 - Grayscale image. + * 2 - RGB triple. + * 3 - Palette. + * 4 - Grayscale with Alpha channel. + * 6 - RGB with Alpha channel. + */ +byte getColorType() { + return colorType; +} + +/** + * Set the image's color type. + * This is limited to the values: + * 0 - Grayscale image. + * 2 - RGB triple. + * 3 - Palette. + * 4 - Grayscale with Alpha channel. + * 6 - RGB with Alpha channel. + */ +void setColorType(byte value) { + reference[COLOR_TYPE_OFFSET] = value; + colorType = value; +} + +/** + * Get the image's compression method. + * This value must be 0. + */ +byte getCompressionMethod() { + return compressionMethod; +} + +/** + * Set the image's compression method. + * This value must be 0. + */ +void setCompressionMethod(byte value) { + reference[COMPRESSION_METHOD_OFFSET] = value; + compressionMethod = value; +} + +/** + * Get the image's filter method. + * This value must be 0. + */ +byte getFilterMethod() { + return filterMethod; +} + +/** + * Set the image's filter method. + * This value must be 0. + */ +void setFilterMethod(byte value) { + reference[FILTER_METHOD_OFFSET] = value; + filterMethod = value; +} + +/** + * Get the image's interlace method. + * This value is limited to: + * 0 - No interlacing used. + * 1 - Adam7 interlacing used. + */ +byte getInterlaceMethod() { + return interlaceMethod; +} + +/** + * Set the image's interlace method. + * This value is limited to: + * 0 - No interlacing used. + * 1 - Adam7 interlacing used. + */ +void setInterlaceMethod(byte value) { + reference[INTERLACE_METHOD_OFFSET] = value; + interlaceMethod = value; +} + +/** + * Answer whether the chunk is a valid IHDR chunk. + */ +void validate(PngFileReadState readState, PngIhdrChunk headerChunk) { + // An IHDR chunk is invalid if any other chunk has + // been read. + if (readState.readIHDR + || readState.readPLTE + || readState.readIDAT + || readState.readIEND) + { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } else { + readState.readIHDR = true; + } + + super.validate(readState, headerChunk); + + if (length != IHDR_DATA_LENGTH) SWT.error(SWT.ERROR_INVALID_IMAGE); + if (compressionMethod != 0) SWT.error(SWT.ERROR_INVALID_IMAGE); + if (interlaceMethod != INTERLACE_METHOD_NONE && + interlaceMethod != INTERLACE_METHOD_ADAM7) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + + bool colorTypeIsValid = false; + for (int i = 0; i < ValidColorTypes.length; i++) { + if (ValidColorTypes[i] == colorType) { + colorTypeIsValid = true; + break; + } + } + if (!colorTypeIsValid) SWT.error(SWT.ERROR_INVALID_IMAGE); + + bool bitDepthIsValid = false; + for (int i = 0; i < ValidBitDepths.length; i++) { + if (ValidBitDepths[i] == bitDepth) { + bitDepthIsValid = true; + break; + } + } + if (!bitDepthIsValid) SWT.error(SWT.ERROR_INVALID_IMAGE); + + if ((colorType == COLOR_TYPE_RGB + || colorType == COLOR_TYPE_RGB_WITH_ALPHA + || colorType == COLOR_TYPE_GRAYSCALE_WITH_ALPHA) + && bitDepth < 8) + { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + + if (colorType == COLOR_TYPE_PALETTE && bitDepth > 8) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } +} + +char[] getColorTypeString() { + switch (colorType) { + case COLOR_TYPE_GRAYSCALE: return "Grayscale"; + case COLOR_TYPE_RGB: return "RGB"; + case COLOR_TYPE_PALETTE: return "Palette"; + case COLOR_TYPE_GRAYSCALE_WITH_ALPHA: return "Grayscale with Alpha"; + case COLOR_TYPE_RGB_WITH_ALPHA: return "RGB with Alpha"; + default: return "Unknown - " ~ cast(char)colorType; + } +} + +char[] getFilterMethodString() { + switch (filterMethod) { + case FILTER_NONE: return "None"; + case FILTER_SUB: return "Sub"; + case FILTER_UP: return "Up"; + case FILTER_AVERAGE: return "Average"; + case FILTER_PAETH: return "Paeth"; + default: return "Unknown"; + } +} + +char[] getInterlaceMethodString() { + switch (interlaceMethod) { + case INTERLACE_METHOD_NONE: return "Not Interlaced"; + case INTERLACE_METHOD_ADAM7: return "Interlaced - ADAM7"; + default: return "Unknown"; + } +} + +override char[] contributeToString() { + return Format( "\n\tWidth: {}\n\tHeight: {}\n\tBit Depth: {}\n\tColor Type: {}\n\tCompression Method: {}\n\tFilter Method: {}\n\tInterlace Method: {}", + width, height, bitDepth, getColorTypeString(), compressionMethod, getFilterMethodString(), getInterlaceMethodString() ); +} + +bool getMustHavePalette() { + return colorType == COLOR_TYPE_PALETTE; +} + +bool getCanHavePalette() { + return colorType != COLOR_TYPE_GRAYSCALE && + colorType != COLOR_TYPE_GRAYSCALE_WITH_ALPHA; +} + +/** + * Answer the pixel size in bits based on the color type + * and bit depth. + */ +int getBitsPerPixel() { + switch (colorType) { + case COLOR_TYPE_RGB_WITH_ALPHA: + return 4 * bitDepth; + case COLOR_TYPE_RGB: + return 3 * bitDepth; + case COLOR_TYPE_GRAYSCALE_WITH_ALPHA: + return 2 * bitDepth; + case COLOR_TYPE_GRAYSCALE: + case COLOR_TYPE_PALETTE: + return bitDepth; + default: + SWT.error(SWT.ERROR_INVALID_IMAGE); + return 0; + } +} + +/** + * Answer the pixel size in bits based on the color type + * and bit depth. + */ +int getSwtBitsPerPixel() { + switch (colorType) { + case COLOR_TYPE_RGB_WITH_ALPHA: + case COLOR_TYPE_RGB: + case COLOR_TYPE_GRAYSCALE_WITH_ALPHA: + return 24; + case COLOR_TYPE_GRAYSCALE: + case COLOR_TYPE_PALETTE: + return Math.min(bitDepth, 8); + default: + SWT.error(SWT.ERROR_INVALID_IMAGE); + return 0; + } +} + +int getFilterByteOffset() { + if (bitDepth < 8) return 1; + return getBitsPerPixel() / 8; +} + +bool usesDirectColor() { + switch (colorType) { + case COLOR_TYPE_GRAYSCALE: + case COLOR_TYPE_GRAYSCALE_WITH_ALPHA: + case COLOR_TYPE_RGB: + case COLOR_TYPE_RGB_WITH_ALPHA: + return true; + default: + return false; + } +} + +PaletteData createGrayscalePalette() { + int depth = Math.min(bitDepth, 8); + int max = (1 << depth) - 1; + int delta = 255 / max; + int gray = 0; + RGB[] rgbs = new RGB[max + 1]; + for (int i = 0; i <= max; i++) { + rgbs[i] = new RGB(gray, gray, gray); + gray += delta; + } + return new PaletteData(rgbs); +} + +PaletteData getPaletteData() { + switch (colorType) { + case COLOR_TYPE_GRAYSCALE: + return createGrayscalePalette(); + case COLOR_TYPE_GRAYSCALE_WITH_ALPHA: + case COLOR_TYPE_RGB: + case COLOR_TYPE_RGB_WITH_ALPHA: + return new PaletteData(0xFF0000, 0xFF00, 0xFF); + default: + return null; + } +} + + + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngInputStream.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngInputStream.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngInputStream; + +import dwt.dwthelper.InputStream; +import dwt.internal.image.PngIdatChunk; +import dwt.internal.image.PngChunkReader; +import dwt.internal.image.PngChunk; + +import tango.core.Exception; +import Math = tango.math.Math; + +public class PngInputStream : InputStream { + alias InputStream.read read; + + PngChunkReader reader; + PngChunk chunk; + int offset, length; + + final static int DATA_OFFSET = 8; + +public this(PngIdatChunk chunk, PngChunkReader reader) { + this.chunk = chunk; + this.reader = reader; + length = chunk.getLength(); + offset = 0; +} + +private bool checkChunk() { + while (offset == length) { + chunk = reader.readNextChunk(); + if (chunk == null) throw new IOException("no data"); + if (chunk.getChunkType() == PngChunk.CHUNK_IEND) return false; + if (chunk.getChunkType() != PngChunk.CHUNK_IDAT) throw new IOException(""); + length = chunk.getLength(); + offset = 0; + } + return true; +} + +public override void close() { + chunk = null; +} + +public override int read() { + if (chunk == null) throw new IOException(""); + if (offset == length && !checkChunk()) return -1; + int b = chunk.reference[DATA_OFFSET + offset] & 0xFF; + offset++; + return b; +} + +public override int read(byte[] b, int off, int len) { + if (chunk == null) throw new IOException(""); + if (offset == length && !checkChunk()) return -1; + len = Math.min(len, length - offset); + b[ off .. off+len ] = chunk.reference[ DATA_OFFSET + offset .. DATA_OFFSET + offset + len ]; + offset += len; + return len; +} +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngLzBlockReader.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngLzBlockReader.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngLzBlockReader; + +import dwt.internal.image.PngDecodingDataStream; +import dwt.internal.image.PngHuffmanTables; + +public class PngLzBlockReader { + bool isLastBlock; + byte compressionType; + int uncompressedBytesRemaining; + PngDecodingDataStream stream; + PngHuffmanTables huffmanTables; + + byte[] window; + int windowIndex; + int copyIndex; + int copyBytesRemaining; + + static const int UNCOMPRESSED = 0; + static const int COMPRESSED_FIXED = 1; + static const int COMPRESSED_DYNAMIC = 2; + + static const int END_OF_COMPRESSED_BLOCK = 256; + static const int FIRST_LENGTH_CODE = 257; + static const int LAST_LENGTH_CODE = 285; + static const int FIRST_DISTANCE_CODE = 1; + static const int LAST_DISTANCE_CODE = 29; + static const int FIRST_CODE_LENGTH_CODE = 4; + static const int LAST_CODE_LENGTH_CODE = 19; + + static const int[] lengthBases = [ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 + ]; + static const int[] extraLengthBits = [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, + ]; + static const int[] distanceBases = [ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, + 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, + 6145, 8193, 12289, 16385, 24577, + ]; + static const int[] extraDistanceBits = [ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + ]; + + +this(PngDecodingDataStream stream) { + this.stream = stream; + isLastBlock = false; +} + +void setWindowSize(int windowSize) { + window = new byte[windowSize]; +} + +void readNextBlockHeader() { + isLastBlock = stream.getNextIdatBit() != 0; + compressionType = cast(byte)(stream.getNextIdatBits(2) & 0xFF); + if (compressionType > 2) stream.error(); + + if (compressionType == UNCOMPRESSED) { + byte b1 = stream.getNextIdatByte(); + byte b2 = stream.getNextIdatByte(); + byte b3 = stream.getNextIdatByte(); + byte b4 = stream.getNextIdatByte(); + if (b1 != ~b3 || b2 != ~b4) stream.error(); + uncompressedBytesRemaining = (b1 & 0xFF) | ((b2 & 0xFF) << 8); + } else if (compressionType == COMPRESSED_DYNAMIC) { + huffmanTables = PngHuffmanTables.getDynamicTables(stream); + } else { + huffmanTables = PngHuffmanTables.getFixedTables(); + } +} + +byte getNextByte() { + if (compressionType == UNCOMPRESSED) { + if (uncompressedBytesRemaining == 0) { + readNextBlockHeader(); + return getNextByte(); + } + uncompressedBytesRemaining--; + return stream.getNextIdatByte(); + } else { + byte value = getNextCompressedByte(); + if (value == END_OF_COMPRESSED_BLOCK) { + if (isLastBlock) stream.error(); + readNextBlockHeader(); + return getNextByte(); + } else { + return value; + } + } +} + +private void assertBlockAtEnd() { + if (compressionType == UNCOMPRESSED) { + if (uncompressedBytesRemaining > 0) stream.error(); + } else if (copyBytesRemaining > 0 || + (huffmanTables.getNextLiteralValue(stream) != END_OF_COMPRESSED_BLOCK)) + { + stream.error(); + } +} +void assertCompressedDataAtEnd() { + assertBlockAtEnd(); + while (!isLastBlock) { + readNextBlockHeader(); + assertBlockAtEnd(); + } +} + +private byte getNextCompressedByte() { + if (copyBytesRemaining > 0) { + byte value = window[copyIndex]; + window[windowIndex] = value; + copyBytesRemaining--; + + copyIndex++; + windowIndex++; + if (copyIndex == window.length) copyIndex = 0; + if (windowIndex == window.length) windowIndex = 0; + + return value; + } + + int value = huffmanTables.getNextLiteralValue(stream); + if (value < END_OF_COMPRESSED_BLOCK) { + window[windowIndex] = cast(byte) (value & 0xFF); + windowIndex++; + if (windowIndex >= window.length) windowIndex = 0; + return cast(byte) (value & 0xFF); + } else if (value == END_OF_COMPRESSED_BLOCK) { + readNextBlockHeader(); + return getNextByte(); + } else if (value <= LAST_LENGTH_CODE) { + int extraBits = extraLengthBits[value - FIRST_LENGTH_CODE]; + int length = lengthBases[value - FIRST_LENGTH_CODE]; + if (extraBits > 0) { + length += stream.getNextIdatBits(extraBits); + } + + value = huffmanTables.getNextDistanceValue(stream); + if (value > LAST_DISTANCE_CODE) stream.error(); + extraBits = extraDistanceBits[value]; + int distance = distanceBases[value]; + if (extraBits > 0) { + distance += stream.getNextIdatBits(extraBits); + } + + copyIndex = windowIndex - distance; + if (copyIndex < 0) copyIndex += window.length; + + copyBytesRemaining = length; + return getNextCompressedByte(); + } else { + stream.error(); + return 0; + } +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngPlteChunk.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngPlteChunk.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngPlteChunk; + + +import dwt.SWT; +import dwt.graphics.PaletteData; +import dwt.graphics.RGB; +import dwt.internal.image.PngChunk; +import dwt.internal.image.PngFileReadState; +import dwt.internal.image.PngIhdrChunk; + +import tango.text.convert.Format; + +class PngPlteChunk : PngChunk { + + int paletteSize; + +this(PaletteData palette) { + super(palette.getRGBs().length * 3); + paletteSize = length / 3; + setType(TYPE_PLTE); + setPaletteData(palette); + setCRC(computeCRC()); +} + +this(byte[] reference){ + super(reference); + paletteSize = length / 3; +} + +int getChunkType() { + return CHUNK_PLTE; +} + +/** + * Get the number of colors in this palette. + */ +int getPaletteSize() { + return paletteSize; +} + +/** + * Get a PaletteData object representing the colors + * stored in this PLTE chunk. + * The result should be cached as the PLTE chunk + * does not store the palette data created. + */ +PaletteData getPaletteData() { + RGB[] rgbs = new RGB[paletteSize]; +// int start = DATA_OFFSET; +// int end = DATA_OFFSET + length; + for (int i = 0; i < rgbs.length; i++) { + int offset = DATA_OFFSET + (i * 3); + int red = reference[offset] & 0xFF; + int green = reference[offset + 1] & 0xFF; + int blue = reference[offset + 2] & 0xFF; + rgbs[i] = new RGB(red, green, blue); + } + return new PaletteData(rgbs); +} + +/** + * Set the data of a PLTE chunk to the colors + * stored in the specified PaletteData object. + */ +void setPaletteData(PaletteData palette) { + RGB[] rgbs = palette.getRGBs(); + for (int i = 0; i < rgbs.length; i++) { + int offset = DATA_OFFSET + (i * 3); + reference[offset] = cast(byte) rgbs[i].red; + reference[offset + 1] = cast(byte) rgbs[i].green; + reference[offset + 2] = cast(byte) rgbs[i].blue; + } +} + +/** + * Answer whether the chunk is a valid PLTE chunk. + */ +void validate(PngFileReadState readState, PngIhdrChunk headerChunk) { + // A PLTE chunk is invalid if no IHDR has been read or if any PLTE, + // IDAT, or IEND chunk has been read. + if (!readState.readIHDR + || readState.readPLTE + || readState.readTRNS + || readState.readIDAT + || readState.readIEND) + { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } else { + readState.readPLTE = true; + } + + super.validate(readState, headerChunk); + + // Palettes cannot be included in grayscale images. + // + // Note: just ignore the palette. +// if (!headerChunk.getCanHavePalette()) SWT.error(SWT.ERROR_INVALID_IMAGE); + + // Palette chunks' data fields must be event multiples + // of 3. Each 3-byte group represents an RGB value. + if (getLength() % 3 != 0) SWT.error(SWT.ERROR_INVALID_IMAGE); + + // Palettes cannot have more entries than 2^bitDepth + // where bitDepth is the bit depth of the image given + // in the IHDR chunk. + if (1 << headerChunk.getBitDepth() < paletteSize) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + + // Palettes cannot have more than 256 entries. + if (256 < paletteSize) SWT.error(SWT.ERROR_INVALID_IMAGE); +} + +override char[] contributeToString() { + return Format("\n\tPalette size:{}", paletteSize ); +} + +} diff -r 3d9bbe0a83a0 -r 7b1ca4eb5763 dwt/internal/image/PngTrnsChunk.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/internal/image/PngTrnsChunk.d Mon Jan 07 00:11:14 2008 +0100 @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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.PngTrnsChunk; + +import dwt.SWT; +import dwt.graphics.PaletteData; +import dwt.graphics.RGB; +import dwt.internal.image.PngChunk; +import dwt.internal.image.PNGFileFormat; +import dwt.internal.image.PngFileReadState; +import dwt.internal.image.PngIhdrChunk; +import dwt.internal.image.PngPlteChunk; + +public class PngTrnsChunk : PngChunk { + static const int TRANSPARENCY_TYPE_PIXEL = 0; + static const int TRANSPARENCY_TYPE_ALPHAS = 1; + static const int RGB_DATA_LENGTH = 6; + +this(RGB rgb) { + super(RGB_DATA_LENGTH); + setType(TYPE_tRNS); + setInt16(DATA_OFFSET, rgb.red); + setInt16(DATA_OFFSET + 2, rgb.green); + setInt16(DATA_OFFSET + 4, rgb.blue); + setCRC(computeCRC()); +} + +this(byte[] reference){ + super(reference); +} + +int getChunkType() { + return CHUNK_tRNS; +} + +void validateLength(PngIhdrChunk header, PngPlteChunk paletteChunk) { + bool valid; + switch (header.getColorType()) { + case PngIhdrChunk.COLOR_TYPE_RGB: + // Three 2-byte values (RGB) + valid = getLength() == 6; + break; + case PngIhdrChunk.COLOR_TYPE_PALETTE: + // Three 2-byte values (RGB) + valid = getLength() <= paletteChunk.getLength(); + break; + case PngIhdrChunk.COLOR_TYPE_GRAYSCALE: + // One 2-byte value + valid = getLength() == 2; + break; + // Cannot use both Alpha and tRNS + case PngIhdrChunk.COLOR_TYPE_RGB_WITH_ALPHA: + case PngIhdrChunk.COLOR_TYPE_GRAYSCALE_WITH_ALPHA: + default: + valid = false; + } + if (!valid) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } +} + +/** + * Answer whether the chunk is a valid tRNS chunk. + */ +void validate(PngFileReadState readState, PngIhdrChunk headerChunk, PngPlteChunk paletteChunk) { + if (!readState.readIHDR + || (headerChunk.getMustHavePalette() && !readState.readPLTE) + || readState.readIDAT + || readState.readIEND) + { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } else { + readState.readTRNS = true; + } + + validateLength(headerChunk, paletteChunk); + + super.validate(readState, headerChunk); +} + + +int getTransparencyType(PngIhdrChunk header) { + if (header.getColorType() == PngIhdrChunk.COLOR_TYPE_PALETTE) { + return TRANSPARENCY_TYPE_ALPHAS; + } + return TRANSPARENCY_TYPE_PIXEL; +} + +/** + * Answer the transparent pixel RGB value. + * This is not valid for palette color types. + * This is not valid for alpha color types. + * This will convert a grayscale value into + * a palette index. + * It will compress a 6 byte RGB into a 3 byte + * RGB. + */ +int getSwtTransparentPixel(PngIhdrChunk header) { + switch (header.getColorType()) { + case PngIhdrChunk.COLOR_TYPE_GRAYSCALE: + int gray = ((reference[DATA_OFFSET] & 0xFF) << 8) + + (reference[DATA_OFFSET + 1] & 0xFF); + if (header.getBitDepth() > 8) { + return PNGFileFormat.compress16BitDepthTo8BitDepth(gray); + } + return gray & 0xFF; + case PngIhdrChunk.COLOR_TYPE_RGB: + int red = ((reference[DATA_OFFSET] & 0xFF) << 8) + | (reference[DATA_OFFSET + 1] & 0xFF); + int green = ((reference[DATA_OFFSET + 2] & 0xFF) << 8) + | (reference[DATA_OFFSET + 3] & 0xFF); + int blue = ((reference[DATA_OFFSET + 4] & 0xFF) << 8) + | (reference[DATA_OFFSET + 5] & 0xFF); + if (header.getBitDepth() > 8) { + red = PNGFileFormat.compress16BitDepthTo8BitDepth(red); + green = PNGFileFormat.compress16BitDepthTo8BitDepth(green); + blue = PNGFileFormat.compress16BitDepthTo8BitDepth(blue); + } + return (red << 16) | (green << 8) | blue; + default: + SWT.error(SWT.ERROR_INVALID_IMAGE); + return -1; + } +} + +/** + * Answer an array of Alpha values that correspond to the + * colors in the palette. + * This is only valid for the COLOR_TYPE_PALETTE color type. + */ +byte[] getAlphaValues(PngIhdrChunk header, PngPlteChunk paletteChunk) { + if (header.getColorType() != PngIhdrChunk.COLOR_TYPE_PALETTE) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + byte[] alphas = new byte[paletteChunk.getPaletteSize()]; + int dataLength = getLength(); + int i = 0; + for (i = 0; i < dataLength; i++) { + alphas[i] = reference[DATA_OFFSET + i]; + } + /** + * Any palette entries which do not have a corresponding + * alpha value in the tRNS chunk are spec'd to have an + * alpha of 255. + */ + for (int j = i; j < alphas.length; j++) { + alphas[j] = cast(byte) 255; + } + return alphas; +} +}