changeset 14:7b1ca4eb5763

file format png
author Frank Benoit <benoit@tionex.de>
date Mon, 07 Jan 2008 00:11:14 +0100
parents 3d9bbe0a83a0
children 0a61cfe9ff23
files dwt/dwthelper/BufferedInputStream.d dwt/internal/image/FileFormat.d dwt/internal/image/PNGFileFormat.d dwt/internal/image/PngChunk.d dwt/internal/image/PngChunkReader.d dwt/internal/image/PngDecodingDataStream.d dwt/internal/image/PngDeflater.d dwt/internal/image/PngEncoder.d dwt/internal/image/PngFileReadState.d dwt/internal/image/PngHuffmanTable.d dwt/internal/image/PngHuffmanTables.d dwt/internal/image/PngIdatChunk.d dwt/internal/image/PngIendChunk.d dwt/internal/image/PngIhdrChunk.d dwt/internal/image/PngInputStream.d dwt/internal/image/PngLzBlockReader.d dwt/internal/image/PngPlteChunk.d dwt/internal/image/PngTrnsChunk.d
diffstat 18 files changed, 3630 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- /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 <keinfarbton@googlemail.com>
+ */
+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();
+    }
+
+
+}
+
+
--- 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.
--- /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; //<RETURN>
+		if ((signature[5] & 0xFF) != 10) return false; //<LINEFEED>
+		if ((signature[6] & 0xFF) != 26) return false; //<CTRL/Z>
+		if ((signature[7] & 0xFF) != 10) return false; //<LINEFEED>
+		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
--- /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;
+}
+
+}
--- /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;
+}
+
+}
--- /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();
+}
+
+}
--- /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();
+
+}
+
+}
--- /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);
+
+	}
+
+}
+
+}
--- /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;
+}
--- /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;
+}
+
+}
--- /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 <LengthCodeTableSize> 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);
+}
+
+}
--- /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];
+}
+
+}
--- /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);
+}
+
+}
--- /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;
+	}
+}
+
+
+
+}
--- /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;
+}
+}
--- /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;
+	}
+}
+
+}
--- /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 );
+}
+
+}
--- /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;
+}
+}