changeset 13:3d9bbe0a83a0

FileFormats
author Frank Benoit <benoit@tionex.de>
date Sun, 06 Jan 2008 22:54:14 +0100
parents 0c78fa47d476
children 7b1ca4eb5763
files dwt/dwthelper/ByteArrayOutputStream.d dwt/dwthelper/Integer.d dwt/internal/image/FileFormat.d dwt/internal/image/GIFFileFormat.d dwt/internal/image/JPEGAppn.d dwt/internal/image/JPEGArithmeticConditioningTable.d dwt/internal/image/JPEGComment.d dwt/internal/image/JPEGDecoder.d dwt/internal/image/JPEGEndOfImage.d dwt/internal/image/JPEGFileFormat.d dwt/internal/image/JPEGFixedSizeSegment.d dwt/internal/image/JPEGFrameHeader.d dwt/internal/image/JPEGHuffmanTable.d dwt/internal/image/JPEGQuantizationTable.d dwt/internal/image/JPEGRestartInterval.d dwt/internal/image/JPEGScanHeader.d dwt/internal/image/JPEGSegment.d dwt/internal/image/JPEGStartOfImage.d dwt/internal/image/JPEGVariableSizeSegment.d dwt/internal/image/LEDataInputStream.d dwt/internal/image/LEDataOutputStream.d dwt/internal/image/LZWCodec.d dwt/internal/image/LZWNode.d dwt/internal/image/OS2BMPFileFormat.d dwt/internal/image/TIFFDirectory.d dwt/internal/image/TIFFFileFormat.d dwt/internal/image/TIFFModifiedHuffmanCodec.d dwt/internal/image/TIFFRandomFileAccess.d dwt/internal/image/WinBMPFileFormat.d dwt/internal/image/WinICOFileFormat.d
diffstat 30 files changed, 13044 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/dwthelper/ByteArrayOutputStream.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,69 @@
+/**
+ * Authors: Frank Benoit <keinfarbton@googlemail.com>
+ */
+module dwt.dwthelper.ByteArrayOutputStream;
+
+public import dwt.dwthelper.OutputStream;
+import dwt.dwthelper.utils;
+
+public class ByteArrayOutputStream : dwt.dwthelper.OutputStream.OutputStream {
+
+    alias dwt.dwthelper.OutputStream.OutputStream.write write;
+
+    protected byte[] buf;
+    protected int count;
+    public this (){
+    }
+
+    public this ( int par_size ){
+    }
+
+    public synchronized void write( int b ){
+        implMissing( __FILE__, __LINE__ );
+    }
+
+    public synchronized void write( byte[] b, int off, int len ){
+        implMissing( __FILE__, __LINE__ );
+    }
+
+    public synchronized void writeTo( dwt.dwthelper.OutputStream.OutputStream out_KEYWORDESCAPE ){
+        implMissing( __FILE__, __LINE__ );
+    }
+
+    public synchronized void reset(){
+        implMissing( __FILE__, __LINE__ );
+    }
+
+    public synchronized byte[] toByteArray(){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+
+    public int size(){
+        implMissing( __FILE__, __LINE__ );
+        return 0;
+    }
+
+    public override char[] toString(){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+
+    public char[] toString( char[] enc ){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+
+    public char[] toString( int hibyte ){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+
+    public  override void close(){
+        implMissing( __FILE__, __LINE__ );
+    }
+
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/dwthelper/Integer.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,118 @@
+/**
+ * Authors: Frank Benoit <keinfarbton@googlemail.com>
+ */
+
+module dwt.dwthelper.Integer;
+
+import dwt.dwthelper.utils;
+
+static import tango.text.convert.Integer;
+
+public final class Integer {
+
+    public static int MIN_VALUE = 0x80000000;
+    public static int MAX_VALUE = 0x7fffffff;
+    public static int SIZE = 32;
+
+    public int value;
+    public this ( int value ){
+        this.value = value;
+    }
+
+    public this ( char[] s ){
+    }
+
+    public static char[] toString( int i, int radix ){
+        switch( radix ){
+        case 2:
+            return toBinaryString(i);
+        case 8:
+            return toOctalString(i);
+        case 10:
+            return toString(i);
+        case 16:
+            return toHexString(i);
+        default:
+            implMissing( __FILE__, __LINE__ );
+            return null;
+        }
+    }
+
+    public static char[] toHexString( int i ){
+        return tango.text.convert.Integer.toString(i, tango.text.convert.Integer.Style.Hex );
+    }
+
+    public static char[] toOctalString( int i ){
+        return tango.text.convert.Integer.toString(i, tango.text.convert.Integer.Style.Octal );
+    }
+
+    public static char[] toBinaryString( int i ){
+        return tango.text.convert.Integer.toString(i, tango.text.convert.Integer.Style.Binary );
+    }
+
+    public static char[] toString( int i ){
+        return tango.text.convert.Integer.toString(i);
+    }
+
+    public static int parseInt( char[] s, int radix ){
+        return tango.text.convert.Integer.parse( s, cast(uint)radix );
+    }
+
+    public static int parseInt( char[] s ){
+        return tango.text.convert.Integer.parse( s );
+    }
+
+    public static Integer valueOf( char[] s, int radix ){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+
+    public static Integer valueOf( char[] s ){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+
+    public static Integer valueOf( int i ){
+        implMissing( __FILE__, __LINE__ );
+        return null;
+    }
+
+    public byte byteValue(){
+        return cast(byte)value;
+    }
+
+    public short shortValue(){
+        return cast(short)value;
+    }
+
+    public int intValue(){
+        return value;
+    }
+
+    public long longValue(){
+        return cast(long)value;
+    }
+
+    public float floatValue(){
+        return cast(float)value;
+    }
+
+    public double doubleValue(){
+        return cast(double)value;
+    }
+
+    public override  hash_t toHash(){
+        return intValue();
+    }
+
+    public override int opEquals( Object obj ){
+        implMissing( __FILE__, __LINE__ );
+        return false;
+    }
+
+    public override char[] toString(){
+        return tango.text.convert.Integer.toString( value );
+    }
+}
+
+
--- a/dwt/internal/image/FileFormat.d	Sun Jan 06 19:36:29 2008 +0100
+++ b/dwt/internal/image/FileFormat.d	Sun Jan 06 22:54:14 2008 +0100
@@ -20,14 +20,14 @@
 public import dwt.dwthelper.InputStream;
 public import dwt.dwthelper.OutputStream;
 
-/+
+import dwt.internal.image.GIFFileFormat;
 import dwt.internal.image.WinBMPFileFormat;
-import dwt.internal.image.GIFFileFormat;
 import dwt.internal.image.WinICOFileFormat;
-import dwt.internal.image.JPEGFileFormat;
-import dwt.internal.image.PNGFileFormat;
 import dwt.internal.image.TIFFFileFormat;
 import dwt.internal.image.OS2BMPFileFormat;
+import dwt.internal.image.JPEGFileFormat;
+/+
+import dwt.internal.image.PNGFileFormat;
 +/
 
 import tango.core.Exception;
@@ -43,20 +43,8 @@
     void unloadIntoByteStream(ImageLoader loader){
     }
 }
-class WinBMPFileFormat : Tmp{
-}
-class GIFFileFormat : Tmp{
-}
-class WinICOFileFormat : Tmp{
-}
-class JPEGFileFormat : Tmp{
-}
 class PNGFileFormat : Tmp{
 }
-class TIFFFileFormat : Tmp{
-}
-class OS2BMPFileFormat : Tmp{
-}
 
 /**
  * Abstract factory class for loading/unloading images from files or streams
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/GIFFileFormat.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,605 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.GIFFileFormat;
+
+public import dwt.internal.image.FileFormat;
+public import dwt.graphics.PaletteData;
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.LZWCodec;
+import dwt.graphics.RGB;
+import dwt.SWT;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoaderEvent;
+import dwt.graphics.ImageLoader;
+import tango.core.Exception;
+
+///FORTING_TYPE
+class Image{}
+
+final class GIFFileFormat : FileFormat {
+	char[] signature;
+	int screenWidth, screenHeight, backgroundPixel, bitsPerPixel, defaultDepth;
+	int disposalMethod = 0;
+	int delayTime = 0;
+	int transparentPixel = -1;
+	int repeatCount = 1;
+
+	static final int GIF_APPLICATION_EXTENSION_BLOCK_ID = 0xFF;
+	static final int GIF_GRAPHICS_CONTROL_BLOCK_ID = 0xF9;
+	static final int GIF_PLAIN_TEXT_BLOCK_ID = 0x01;
+	static final int GIF_COMMENT_BLOCK_ID = 0xFE;
+	static final int GIF_EXTENSION_BLOCK_ID = 0x21;
+	static final int GIF_IMAGE_BLOCK_ID = 0x2C;
+	static final int GIF_TRAILER_ID = 0x3B;
+	static final byte[] GIF89a = cast(byte[])"GIF89a";
+	static final byte[] NETSCAPE2_0 = cast(byte[])"NETSCAPE2.0";
+
+	/**
+	 * Answer a palette containing numGrays
+	 * shades of gray, ranging from black to white.
+	 */
+	static PaletteData grayRamp(int numGrays) {
+		int n = numGrays - 1;
+		RGB[] colors = new RGB[numGrays];
+		for (int i = 0; i < numGrays; i++) {
+			int intensity = cast(byte)((i * 3) * 256 / n);
+			colors[i] = new RGB(intensity, intensity, intensity);
+		}
+		return new PaletteData(colors);
+	}
+
+	bool isFileFormat(LEDataInputStream stream) {
+		try {
+			byte[3] signature;
+			stream.read(signature);
+			stream.unread(signature);
+			return signature == cast(byte[])"GIF"; //$NON-NLS-1$
+		} catch (Exception e) {
+			return false;
+		}
+	}
+
+	/**
+	 * Load the GIF image(s) stored in the input stream.
+	 * Return an array of ImageData representing the image(s).
+	 */
+	ImageData[] loadFromByteStream() {
+		byte[3] signatureBytes;
+		byte[3] versionBytes;
+		byte[7] block;
+		try {
+			inputStream.read(signatureBytes);
+			signature = cast(char[])signatureBytes.dup;
+			if (signature != "GIF") //$NON-NLS-1$
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+
+			inputStream.read(versionBytes);
+
+			inputStream.read(block);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		screenWidth = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8);
+		loader.logicalScreenWidth = screenWidth;
+		screenHeight = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8);
+		loader.logicalScreenHeight = screenHeight;
+		byte bitField = block[4];
+		backgroundPixel = block[5] & 0xFF;
+		//aspect = block[6] & 0xFF;
+		bitsPerPixel = ((bitField >> 4) & 0x07) + 1;
+		defaultDepth = (bitField & 0x7) + 1;
+		PaletteData palette = null;
+		if ((bitField & 0x80) != 0) {
+			// Global palette.
+			//sorted = (bitField & 0x8) != 0;
+			palette = readPalette(1 << defaultDepth);
+		} else {
+			// No global palette.
+			//sorted = false;
+			backgroundPixel = -1;
+			defaultDepth = bitsPerPixel;
+		}
+		loader.backgroundPixel = backgroundPixel;
+
+		getExtensions();
+		int id = readID();
+		ImageData[] images = new ImageData[0];
+		while (id == GIF_IMAGE_BLOCK_ID) {
+			ImageData image = readImageBlock(palette);
+			if (loader.hasListeners()) {
+				loader.notifyListeners(new ImageLoaderEvent(loader, image, 3, true));
+			}
+            images ~= image;
+			try {
+				/* Read the 0-byte terminator at the end of the image. */
+				id = inputStream.read();
+				if (id > 0) {
+					/* We read the terminator earlier. */
+                    byte[1] arr;
+                    arr[0] = id;
+					inputStream.unread( arr );
+				}
+			} catch (IOException e) {
+				SWT.error(SWT.ERROR_IO, e);
+			}
+			getExtensions();
+			id = readID();
+		}
+		return images;
+	}
+
+	/**
+	 * Read and return the next block or extension identifier from the file.
+	 */
+	int readID() {
+		try {
+			return inputStream.read();
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		return -1;
+	}
+
+	/**
+	 * Read extensions until an image descriptor appears.
+	 * In the future, if we care about the extensions, they
+	 * should be properly grouped with the image data before
+	 * which they appeared. Right now, the interesting parts
+	 * of some extensions are kept, but the rest is discarded.
+	 * Throw an error if an error occurs.
+	 */
+	void getExtensions() {
+		int id = readID();
+		while (id != GIF_IMAGE_BLOCK_ID && id != GIF_TRAILER_ID && id > 0) {
+			if (id == GIF_EXTENSION_BLOCK_ID) {
+				readExtension();
+			} else {
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+			}
+			id = readID();
+		}
+		if (id == GIF_IMAGE_BLOCK_ID || id == GIF_TRAILER_ID) {
+			try {
+                byte[1] arr;
+                arr[0] = id;
+				inputStream.unread(arr);
+			} catch (IOException e) {
+				SWT.error(SWT.ERROR_IO, e);
+			}
+		}
+	}
+
+	/**
+	 * Read a control extension.
+	 * Return the extension block data.
+	 */
+	byte[] readExtension() {
+		int extensionID = readID();
+		if (extensionID == GIF_COMMENT_BLOCK_ID)
+			return readCommentExtension();
+		if (extensionID == GIF_PLAIN_TEXT_BLOCK_ID)
+			return readPlainTextExtension();
+		if (extensionID == GIF_GRAPHICS_CONTROL_BLOCK_ID)
+			return readGraphicsControlExtension();
+		if (extensionID == GIF_APPLICATION_EXTENSION_BLOCK_ID)
+			return readApplicationExtension();
+		// Otherwise, we don't recognize the block. If the
+		// field size is correct, we can just skip over
+		// the block contents.
+		try {
+			int extSize = inputStream.read();
+			if (extSize < 0) {
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+			}
+			byte[] ext = new byte[extSize];
+			inputStream.read(ext, 0, extSize);
+			return ext;
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+			return null;
+		}
+	}
+
+	/**
+	 * We have just read the Comment extension identifier
+	 * from the input stream. Read in the rest of the comment
+	 * and return it. GIF comment blocks are variable size.
+	 */
+	byte[] readCommentExtension() {
+		try {
+			byte[] comment = new byte[0];
+			byte[] block = new byte[255];
+			int size = inputStream.read();
+			while ((size > 0) && (inputStream.read(block, 0, size) != -1)) {
+                comment ~= block[ 0 .. size ];
+				size = inputStream.read();
+			}
+			return comment;
+		} catch (TracedException e) {
+			SWT.error(SWT.ERROR_IO, e);
+			return null;
+		}
+	}
+
+	/**
+	 * We have just read the PlainText extension identifier
+	 * from the input stream. Read in the plain text info and text,
+	 * and return the text. GIF plain text blocks are variable size.
+	 */
+	byte[] readPlainTextExtension() {
+		try {
+			// Read size of block = 0x0C.
+			inputStream.read();
+			// Read the text information (x, y, width, height, colors).
+			byte[] info = new byte[12];
+			inputStream.read(info);
+			// Read the text.
+			byte[] text = new byte[0];
+			byte[] block = new byte[255];
+			int size = inputStream.read();
+			while ((size > 0) && (inputStream.read(block, 0, size) != -1)) {
+                text ~= block[ 0 .. size ];
+				size = inputStream.read();
+			}
+			return text;
+		} catch (TracedException e) {
+			SWT.error(SWT.ERROR_IO, e);
+			return null;
+		}
+	}
+
+	/**
+	 * We have just read the GraphicsControl extension identifier
+	 * from the input stream. Read in the control information, store
+	 * it, and return it.
+	 */
+	byte[] readGraphicsControlExtension() {
+		try {
+			// Read size of block = 0x04.
+			inputStream.read();
+			// Read the control block.
+			byte[] controlBlock = new byte[4];
+			inputStream.read(controlBlock);
+			byte bitField = controlBlock[0];
+			// Store the user input field.
+			//userInput = (bitField & 0x02) != 0;
+			// Store the disposal method.
+			disposalMethod = (bitField >> 2) & 0x07;
+			// Store the delay time.
+			delayTime = (controlBlock[1] & 0xFF) | ((controlBlock[2] & 0xFF) << 8);
+			// Store the transparent color.
+			if ((bitField & 0x01) != 0) {
+				transparentPixel = controlBlock[3] & 0xFF;
+			} else {
+				transparentPixel = -1;
+			}
+			// Read block terminator.
+			inputStream.read();
+			return controlBlock;
+		} catch (TracedException e) {
+			SWT.error(SWT.ERROR_IO, e);
+			return null;
+		}
+	}
+
+	/**
+	 * We have just read the Application extension identifier
+	 * from the input stream.  Read in the rest of the extension,
+	 * look for and store 'number of repeats', and return the data.
+	 */
+	byte[] readApplicationExtension() {
+		try {
+			// Read size of block = 0x0B.
+			inputStream.read();
+			// Read application identifier.
+			byte[] applicationBytes = new byte[8];
+			inputStream.read(applicationBytes);
+			char[] application = cast(char[])(applicationBytes.dup);
+			// Read authentication code.
+			byte[] authenticationBytes = new byte[3];
+			inputStream.read(authenticationBytes);
+			char[] authentication = cast(char[])(authenticationBytes.dup);
+			// Read application data.
+			byte[] data = new byte[0];
+			byte[] block = new byte[255];
+			int size = inputStream.read();
+			while ((size > 0) && (inputStream.read(block, 0, size) != -1)) {
+                data ~= block[ 0 .. size ];
+				size = inputStream.read();
+			}
+			// Look for the NETSCAPE 'repeat count' field for an animated GIF.
+			if (application=="NETSCAPE" && authentication=="2.0" && data[0] == 01) { //$NON-NLS-1$ //$NON-NLS-2$
+				repeatCount = (data[1] & 0xFF) | ((data[2] & 0xFF) << 8);
+				loader.repeatCount = repeatCount;
+			}
+			return data;
+		} catch (TracedException e) {
+			SWT.error(SWT.ERROR_IO, e);
+			return null;
+		}
+	}
+
+	/**
+	 * Return a DeviceIndependentImage representing the
+	 * image block at the current position in the input stream.
+	 * Throw an error if an error occurs.
+	 */
+	ImageData readImageBlock(PaletteData defaultPalette) {
+		int depth;
+		PaletteData palette;
+		byte[] block = new byte[9];
+		try {
+			inputStream.read(block);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		int left = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8);
+		int top = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8);
+		int width = (block[4] & 0xFF) | ((block[5] & 0xFF) << 8);
+		int height = (block[6] & 0xFF) | ((block[7] & 0xFF) << 8);
+		byte bitField = block[8];
+		bool interlaced = (bitField & 0x40) != 0;
+		//bool sorted = (bitField & 0x20) != 0;
+		if ((bitField & 0x80) != 0) {
+			// Local palette.
+			depth = (bitField & 0x7) + 1;
+			palette = readPalette(1 << depth);
+		} else {
+			// No local palette.
+			depth = defaultDepth;
+			palette = defaultPalette;
+		}
+		/* Work around: Ignore the case where a GIF specifies an
+		 * invalid index for the transparent pixel that is larger
+		 * than the number of entries in the palette. */
+		if (transparentPixel > 1 << depth) {
+			transparentPixel = -1;
+		}
+		// Promote depth to next highest supported value.
+		if (!(depth == 1 || depth == 4 || depth == 8)) {
+			if (depth < 4)
+				depth = 4;
+			else
+				depth = 8;
+		}
+		if (palette == null) {
+			palette = grayRamp(1 << depth);
+		}
+		int initialCodeSize = -1;
+		try {
+			initialCodeSize = inputStream.read();
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		if (initialCodeSize < 0) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		ImageData image = ImageData.internal_new(
+			width,
+			height,
+			depth,
+			palette,
+			4,
+			null,
+			0,
+			null,
+			null,
+			-1,
+			transparentPixel,
+			SWT.IMAGE_GIF,
+			left,
+			top,
+			disposalMethod,
+			delayTime);
+		LZWCodec codec = new LZWCodec();
+		codec.decode(inputStream, loader, image, interlaced, initialCodeSize);
+		return image;
+	}
+
+	/**
+	 * Read a palette from the input stream.
+	 */
+	PaletteData readPalette(int numColors) {
+		byte[] bytes = new byte[numColors * 3];
+		try {
+			if (inputStream.read(bytes) != bytes.length)
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		RGB[] colors = new RGB[numColors];
+		for (int i = 0; i < numColors; i++)
+			colors[i] = new RGB(bytes[i*3] & 0xFF,
+				bytes[i*3+1] & 0xFF, bytes[i*3+2] & 0xFF);
+		return new PaletteData(colors);
+	}
+
+	void unloadIntoByteStream(ImageLoader loader) {
+
+ 		/* Step 1: Acquire GIF parameters. */
+		ImageData[] data = loader.data;
+		int frameCount = data.length;
+		bool multi = frameCount > 1;
+		ImageData firstImage = data[0];
+		int logicalScreenWidth = multi ? loader.logicalScreenWidth : firstImage.width;
+		int logicalScreenHeight = multi ? loader.logicalScreenHeight : firstImage.height;
+		int backgroundPixel = loader.backgroundPixel;
+		int depth = firstImage.depth;
+		PaletteData palette = firstImage.palette;
+		RGB[] colors = palette.getRGBs();
+		short globalTable = 1;
+
+		/* Step 2: Check for validity and global/local color map. */
+		if (!(depth == 1 || depth == 4 || depth == 8)) {
+			SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+		}
+		for (int i=0; i<frameCount; i++) {
+			if (data[i].palette.isDirect) {
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+			}
+			if (multi) {
+				if (!(data[i].height <= logicalScreenHeight && data[i].width <= logicalScreenWidth && data[i].depth == depth)) {
+					SWT.error(SWT.ERROR_INVALID_IMAGE);
+				}
+				if (globalTable == 1) {
+					RGB rgbs[] = data[i].palette.getRGBs();
+					if (rgbs.length != colors.length) {
+						globalTable = 0;
+					} else {
+						for (int j=0; j<colors.length; j++) {
+							if (!(rgbs[j].red == colors[j].red &&
+								rgbs[j].green == colors[j].green &&
+								rgbs[j].blue == colors[j].blue))
+									globalTable = 0;
+						}
+					}
+				}
+			}
+		}
+
+		try {
+ 			/* Step 3: Write the GIF89a Header and Logical Screen Descriptor. */
+			outputStream.write(GIF89a);
+			int bitField = globalTable*128 + (depth-1)*16 + depth-1;
+			outputStream.writeShort(cast(short)logicalScreenWidth);
+			outputStream.writeShort(cast(short)logicalScreenHeight);
+			outputStream.write(bitField);
+			outputStream.write(backgroundPixel);
+			outputStream.write(0); // Aspect ratio is 1:1
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+
+		/* Step 4: Write Global Color Table if applicable. */
+		if (globalTable == 1) {
+			writePalette(palette, depth);
+		}
+
+		/* Step 5: Write Application Extension if applicable. */
+		if (multi) {
+			int repeatCount = loader.repeatCount;
+			try {
+				outputStream.write(GIF_EXTENSION_BLOCK_ID);
+				outputStream.write(GIF_APPLICATION_EXTENSION_BLOCK_ID);
+				outputStream.write(NETSCAPE2_0.length);
+				outputStream.write(NETSCAPE2_0);
+				outputStream.write(3); // Three bytes follow
+				outputStream.write(1); // Extension type
+				outputStream.writeShort(cast(short) repeatCount);
+				outputStream.write(0); // Block terminator
+			} catch (IOException e) {
+				SWT.error(SWT.ERROR_IO, e);
+			}
+		}
+
+		for (int frame=0; frame<frameCount; frame++) {
+
+			/* Step 6: Write Graphics Control Block for each frame if applicable. */
+			if (multi || data[frame].transparentPixel != -1) {
+				writeGraphicsControlBlock(data[frame]);
+			}
+
+			/* Step 7: Write Image Header for each frame. */
+			int x = data[frame].x;
+			int y = data[frame].y;
+			int width = data[frame].width;
+			int height = data[frame].height;
+			try {
+				outputStream.write(GIF_IMAGE_BLOCK_ID);
+				byte[] block = new byte[9];
+				block[0] = cast(byte)(x & 0xFF);
+				block[1] = cast(byte)((x >> 8) & 0xFF);
+				block[2] = cast(byte)(y & 0xFF);
+				block[3] = cast(byte)((y >> 8) & 0xFF);
+				block[4] = cast(byte)(width & 0xFF);
+				block[5] = cast(byte)((width >> 8) & 0xFF);
+				block[6] = cast(byte)(height & 0xFF);
+				block[7] = cast(byte)((height >> 8) & 0xFF);
+				block[8] = cast(byte)(globalTable == 0 ? (depth-1) | 0x80 : 0x00);
+				outputStream.write(block);
+			} catch (IOException e) {
+				SWT.error(SWT.ERROR_IO, e);
+			}
+
+			/* Step 8: Write Local Color Table for each frame if applicable. */
+			if (globalTable == 0) {
+				writePalette(data[frame].palette, depth);
+			}
+
+			/* Step 9: Write the actual data for each frame. */
+			try {
+				outputStream.write(depth); // Minimum LZW Code size
+			} catch (IOException e) {
+				SWT.error(SWT.ERROR_IO, e);
+			}
+			(new LZWCodec()).encode(outputStream, data[frame]);
+		}
+
+		/* Step 10: Write GIF terminator. */
+		try {
+			outputStream.write(0x3B);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+
+	/**
+	 * Write out a GraphicsControlBlock to describe
+	 * the specified device independent image.
+	 */
+	void writeGraphicsControlBlock(ImageData image) {
+		try {
+			outputStream.write(GIF_EXTENSION_BLOCK_ID);
+			outputStream.write(GIF_GRAPHICS_CONTROL_BLOCK_ID);
+			byte[] gcBlock = new byte[4];
+			gcBlock[0] = 0;
+			gcBlock[1] = 0;
+			gcBlock[2] = 0;
+			gcBlock[3] = 0;
+			if (image.transparentPixel != -1) {
+				gcBlock[0] = cast(byte)0x01;
+				gcBlock[3] = cast(byte)image.transparentPixel;
+			}
+			if (image.disposalMethod != 0) {
+				gcBlock[0] |= cast(byte)((image.disposalMethod & 0x07) << 2);
+			}
+			if (image.delayTime != 0) {
+				gcBlock[1] = cast(byte)(image.delayTime & 0xFF);
+				gcBlock[2] = cast(byte)((image.delayTime >> 8) & 0xFF);
+			}
+			outputStream.write(cast(byte)gcBlock.length);
+			outputStream.write(gcBlock);
+			outputStream.write(0); // Block terminator
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+
+	/**
+	 * Write the specified palette to the output stream.
+	 */
+	void writePalette(PaletteData palette, int depth) {
+		byte[] bytes = new byte[(1 << depth) * 3];
+		int offset = 0;
+		for (int i = 0; i < palette.colors.length; i++) {
+			RGB color = palette.colors[i];
+			bytes[offset] = cast(byte)color.red;
+			bytes[offset + 1] = cast(byte)color.green;
+			bytes[offset + 2] = cast(byte)color.blue;
+			offset += 3;
+		}
+		try {
+			outputStream.write(bytes);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGAppn.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGAppn;
+
+import dwt.internal.image.JPEGVariableSizeSegment;
+import dwt.internal.image.JPEGFileFormat;
+import dwt.internal.image.LEDataInputStream;
+
+final class JPEGAppn : JPEGVariableSizeSegment {
+
+	public this(byte[] reference) {
+		super(reference);
+	}
+
+	public this(LEDataInputStream byteStream) {
+		super(byteStream);
+	}
+
+	public bool verify() {
+		int marker = getSegmentMarker();
+		return marker >= JPEGFileFormat.APP0 && marker <= JPEGFileFormat.APP15;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGArithmeticConditioningTable.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGArithmeticConditioningTable;
+
+import dwt.internal.image.JPEGVariableSizeSegment;
+import dwt.internal.image.JPEGFileFormat;
+import dwt.internal.image.LEDataInputStream;
+
+final class JPEGArithmeticConditioningTable : JPEGVariableSizeSegment {
+
+	public this(LEDataInputStream byteStream) {
+		super(byteStream);
+	}
+
+	public int signature() {
+		return JPEGFileFormat.DAC;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGComment.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGComment;
+
+import dwt.internal.image.JPEGVariableSizeSegment;
+import dwt.internal.image.JPEGFileFormat;
+import dwt.internal.image.LEDataInputStream;
+
+final class JPEGComment : JPEGVariableSizeSegment {
+
+	public this(byte[] reference) {
+		super(reference);
+	}
+
+	public this(LEDataInputStream byteStream) {
+		super(byteStream);
+	}
+
+	public int signature() {
+		return JPEGFileFormat.COM;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGDecoder.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,6386 @@
+/*******************************************************************************
+ * 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.JPEGDecoder;
+
+import dwt.SWT;
+import dwt.dwthelper.InputStream;
+import dwt.internal.image.LEDataInputStream;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoader;
+import dwt.graphics.ImageLoaderEvent;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+
+import tango.core.Exception;
+import tango.util.Convert;
+import Math = tango.math.Math;
+
+public class JPEGDecoder {
+
+	static const int DCTSIZE = 8;
+	static const int DCTSIZE2 = 64;
+	static const int NUM_QUANT_TBLS = 4;
+	static const int NUM_HUFF_TBLS = 4;
+	static const int NUM_ARITH_TBLS = 16;
+	static const int MAX_COMPS_IN_SCAN = 4;
+	static const int MAX_COMPONENTS = 10;
+	static const int MAX_SAMP_FACTOR = 4;
+	static const int D_MAX_BLOCKS_IN_MCU = 10;
+	static const int HUFF_LOOKAHEAD = 8;
+	static const int MAX_Q_COMPS = 4;
+	static const int IFAST_SCALE_BITS = 2;
+	static const int MAXJSAMPLE = 255;
+	static const int CENTERJSAMPLE = 128;
+	static const int MIN_GET_BITS = 32-7;
+	static const int INPUT_BUFFER_SIZE = 4096;
+
+	static const int SCALEBITS = 16;	/* speediest right-shift on some machines */
+	static const int ONE_HALF = 1 << (SCALEBITS-1);
+
+	static const int RGB_RED = 2;	/* Offset of Red in an RGB scanline element */
+	static const int RGB_GREEN = 1;	/* Offset of Green */
+	static const int RGB_BLUE = 0;	/* Offset of Blue */
+	static const int RGB_PIXELSIZE = 3;
+
+	static const int JBUF_PASS_THRU = 0;
+	static const int JBUF_SAVE_SOURCE = 1;	/* Run source subobject only, save output */
+	static const int JBUF_CRANK_DEST = 2;	/* Run dest subobject only, using saved data */
+	static const int JBUF_SAVE_AND_PASS = 3;
+
+	static const int JPEG_MAX_DIMENSION = 65500;
+	static const int BITS_IN_JSAMPLE = 8;
+
+	static const int JDITHER_NONE = 0;		/* no dithering */
+	static const int JDITHER_ORDERED = 1;	/* simple ordered dither */
+	static const int JDITHER_FS = 2;
+
+	static const int JDCT_ISLOW = 0;	/* slow but accurate integer algorithm */
+	static const int JDCT_IFAST = 1;	/* faster, less accurate integer method */
+	static const int JDCT_FLOAT = 2;	/* floating-point: accurate, fast on fast HW */
+	static const int JDCT_DEFAULT = JDCT_ISLOW;
+
+	static const int JCS_UNKNOWN = 0;		/* error/unspecified */
+	static const int JCS_GRAYSCALE = 1;		/* monochrome */
+	static const int JCS_RGB = 2;		/* red/green/blue */
+	static const int JCS_YCbCr = 3;		/* Y/Cb/Cr (also known as YUV) */
+	static const int JCS_CMYK = 4;		/* C/M/Y/K */
+	static const int JCS_YCCK = 5;		/* Y/Cb/Cr/K */
+
+	static const int SAVED_COEFS = 6;
+	static const int Q01_POS = 1;
+	static const int Q10_POS = 8;
+	static const int Q20_POS = 16;
+	static const int Q11_POS = 9;
+	static const int Q02_POS = 2;
+
+	static const int CTX_PREPARE_FOR_IMCU = 0;	/* need to prepare for MCU row */
+	static const int CTX_PROCESS_IMCU = 1;	/* feeding iMCU to postprocessor */
+	static const int CTX_POSTPONED_ROW = 2;	/* feeding postponed row group */
+
+	static const int APP0_DATA_LEN = 14;	/* Length of interesting data in APP0 */
+	static const int APP14_DATA_LEN = 12;	/* Length of interesting data in APP14 */
+	static const int APPN_DATA_LEN = 14;	/* Must be the largest of the above!! */
+
+	/* markers */
+	static const int M_SOF0 = 0xc0;
+	static const int M_SOF1 = 0xc1;
+	static const int M_SOF2 = 0xc2;
+	static const int M_SOF3 = 0xc3;
+	static const int M_SOF5 = 0xc5;
+	static const int M_SOF6 = 0xc6;
+	static const int M_SOF7 = 0xc7;
+	static const int M_JPG = 0xc8;
+	static const int M_SOF9 = 0xc9;
+	static const int M_SOF10 = 0xca;
+	static const int M_SOF11 = 0xcb;
+	static const int M_SOF13 = 0xcd;
+	static const int M_SOF14 = 0xce;
+	static const int M_SOF15 = 0xcf;
+	static const int M_DHT = 0xc4;
+	static const int M_DAC = 0xcc;
+	static const int M_RST0 = 0xd0;
+	static const int M_RST1 = 0xd1;
+	static const int M_RST2	= 0xd2;
+	static const int M_RST3 = 0xd3;
+	static const int M_RST4 = 0xd4;
+	static const int M_RST5 = 0xd5;
+	static const int M_RST6 = 0xd6;
+	static const int M_RST7 = 0xd7;
+	static const int M_SOI = 0xd8;
+	static const int M_EOI = 0xd9;
+	static const int M_SOS = 0xda;
+	static const int M_DQT = 0xdb;
+	static const int M_DNL = 0xdc;
+	static const int M_DRI = 0xdd;
+	static const int M_DHP = 0xde;
+	static const int M_EXP = 0xdf;
+	static const int M_APP0 = 0xe0;
+	static const int M_APP1 = 0xe1;
+	static const int M_APP2 = 0xe2;
+	static const int M_APP3 = 0xe3;
+	static const int M_APP4 = 0xe4;
+	static const int M_APP5 = 0xe5;
+	static const int M_APP6 = 0xe6;
+	static const int M_APP7 = 0xe7;
+	static const int M_APP8 = 0xe8;
+	static const int M_APP9 = 0xe9;
+	static const int M_APP10 = 0xea;
+	static const int M_APP11 = 0xeb;
+	static const int M_APP12 = 0xec;
+	static const int M_APP13 = 0xed;
+	static const int M_APP14 = 0xee;
+	static const int M_APP15 = 0xef;
+ 	static const int M_JPG0 = 0xf0;
+	static const int M_JPG13 = 0xfd;
+	static const int M_COM = 0xfe;
+	static const int M_TEM = 0x01;
+	static const int M_ERROR = 0x100;
+
+	/* Values of global_state field (jdapi.c has some dependencies on ordering!) */
+	static const int CSTATE_START = 100;	/* after create_compress */
+	static const int CSTATE_SCANNING = 101;	/* start_compress done, write_scanlines OK */
+	static const int CSTATE_RAW_OK = 102;	/* start_compress done, write_raw_data OK */
+	static const int CSTATE_WRCOEFS = 103;	/* jpeg_write_coefficients done */
+	static const int DSTATE_START = 200;	/* after create_decompress */
+	static const int DSTATE_INHEADER = 201;	/* reading header markers, no SOS yet */
+	static const int DSTATE_READY = 202;	/* found SOS, ready for start_decompress */
+	static const int DSTATE_PRELOAD = 203;	/* reading multiscan file in start_decompress*/
+	static const int DSTATE_PRESCAN = 204;	/* performing dummy pass for 2-pass quant */
+	static const int DSTATE_SCANNING = 205;	/* start_decompress done, read_scanlines OK */
+	static const int DSTATE_RAW_OK = 206;	/* start_decompress done, read_raw_data OK */
+	static const int DSTATE_BUFIMAGE = 207;	/* expecting jpeg_start_output */
+	static const int DSTATE_BUFPOST = 208;	/* looking for SOS/EOI in jpeg_finish_output */
+	static const int DSTATE_RDCOEFS = 209;	/* reading file in jpeg_read_coefficients */
+	static const int DSTATE_STOPPING = 210;	/* looking for EOI in jpeg_finish_decompress */
+
+	static const int JPEG_REACHED_SOS = 1; /* Reached start of new scan */
+	static const int JPEG_REACHED_EOI = 2; /* Reached end of image */
+	static const int JPEG_ROW_COMPLETED = 3; /* Completed one iMCU row */
+	static const int JPEG_SCAN_COMPLETED = 4; /* Completed last iMCU row of a scan */
+
+	static const int JPEG_SUSPENDED = 0; /* Suspended due to lack of input data */
+	static const int JPEG_HEADER_OK = 1; /* Found valid image datastream */
+	static const int JPEG_HEADER_TABLES_ONLY = 2; /* Found valid table-specs-only datastream */
+
+	/* Function pointers */
+	static const int DECOMPRESS_DATA = 0;
+	static const int DECOMPRESS_SMOOTH_DATA = 1;
+	static const int DECOMPRESS_ONEPASS = 2;
+
+	static const int CONSUME_DATA = 0;
+	static const int DUMMY_CONSUME_DATA = 1;
+
+	static const int PROCESS_DATA_SIMPLE_MAIN = 0;
+	static const int PROCESS_DATA_CONTEXT_MAIN = 1;
+	static const int PROCESS_DATA_CRANK_POST = 2;
+
+	static const int POST_PROCESS_1PASS = 0;
+	static const int POST_PROCESS_DATA_UPSAMPLE = 1;
+
+	static const int NULL_CONVERT = 0;
+	static const int GRAYSCALE_CONVERT = 1;
+	static const int YCC_RGB_CONVERT = 2;
+	static const int GRAY_RGB_CONVERT = 3;
+	static const int YCCK_CMYK_CONVERT = 4;
+
+	static const int NOOP_UPSAMPLE = 0;
+	static const int FULLSIZE_UPSAMPLE = 1;
+	static const int H2V1_FANCY_UPSAMPLE = 2;
+	static const int H2V1_UPSAMPLE = 3;
+	static const int H2V2_FANCY_UPSAMPLE = 4;
+	static const int H2V2_UPSAMPLE = 5;
+	static const int INT_UPSAMPLE = 6;
+
+	static const int INPUT_CONSUME_INPUT = 0;
+	static const int COEF_CONSUME_INPUT = 1;
+
+	static int extend_test[] =	 /* entry n is 2**(n-1) */
+	[
+		0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+		0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000
+	];
+
+	static int extend_offset[] = /* entry n is (-1 << n) + 1 */
+	[
+		0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1,
+		((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1,
+		((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1,
+		((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1
+	];
+
+	static int jpeg_natural_order[] = [
+		0,	1,	8, 16,	9,	2,	3, 10,
+		17, 24, 32, 25, 18, 11,	4,	5,
+		12, 19, 26, 33, 40, 48, 41, 34,
+		27, 20, 13,	6,	7, 14, 21, 28,
+		35, 42, 49, 56, 57, 50, 43, 36,
+		29, 22, 15, 23, 30, 37, 44, 51,
+		58, 59, 52, 45, 38, 31, 39, 46,
+		53, 60, 61, 54, 47, 55, 62, 63,
+		63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */
+		63, 63, 63, 63, 63, 63, 63, 63
+	];
+
+	static final class JQUANT_TBL {
+		/* This array gives the coefficient quantizers in natural array order
+		 * (not the zigzag order in which they are stored in a JPEG DQT marker).
+		 * CAUTION: IJG versions prior to v6a kept this array in zigzag order.
+		 */
+		short[DCTSIZE2] quantval;// = new short[DCTSIZE2];	/* quantization step for each coefficient */
+		/* This field is used only during compression.	It's initialized false when
+		 * the table is created, and set true when it's been output to the file.
+		 * You could suppress output of a table by setting this to true.
+		 * (See jpeg_suppress_tables for an example.)
+		 */
+		bool sent_table;		/* true when table has been output */
+	}
+
+	static final class JHUFF_TBL {
+		/* These two fields directly represent the contents of a JPEG DHT marker */
+		byte[17] bits;// = new byte[17]; /* bits[k] = # of symbols with codes of */
+									/* length k bits; bits[0] is unused */
+		byte[256] huffval;// = new byte[256];		/* The symbols, in order of incr code length */
+		/* This field is used only during compression.	It's initialized false when
+		 * the table is created, and set true when it's been output to the file.
+		 * You could suppress output of a table by setting this to true.
+		 * (See jpeg_suppress_tables for an example.)
+		 */
+		bool sent_table;		/* true when table has been output */
+	}
+
+	static final class bitread_perm_state {		/* Bitreading state saved across MCUs */
+		int get_buffer;	/* current bit-extraction buffer */
+		int bits_left;		/* # of unused bits in it */
+	}
+
+	static final class bitread_working_state {		/* Bitreading working state within an MCU */
+		/* Current data source location */
+		/* We need a copy, rather than munging the original, in case of suspension */
+		byte[] buffer; /* => next byte to read from source */
+		int bytes_offset;
+		int bytes_in_buffer;	/* # of bytes remaining in source buffer */
+		/* Bit input buffer --- note these values are kept in register variables,
+		 * not in this struct, inside the inner loops.
+		 */
+		int get_buffer;	/* current bit-extraction buffer */
+		int bits_left;		/* # of unused bits in it */
+		/* Pointer needed by jpeg_fill_bit_buffer. */
+		jpeg_decompress_struct cinfo;	/* back link to decompress master record */
+	}
+
+	static final class savable_state {
+		int EOBRUN; //Note that this is only used in the progressive case
+		int[MAX_COMPS_IN_SCAN] last_dc_val;// = new int[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
+	}
+
+	static final class d_derived_tbl {
+		/* Basic tables: (element [0] of each array is unused) */
+		int[18] maxcode;// = new int[18];		/* largest code of length k (-1 if none) */
+		/* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */
+		int[17] valoffset;// = new int[17];		/* huffval[] offset for codes of length k */
+		/* valoffset[k] = huffval[] index of 1st symbol of code length k, less
+		 * the smallest code of length k; so given a code of length k, the
+		 * corresponding symbol is huffval[code + valoffset[k]]
+		 */
+
+		/* Link to public Huffman table (needed only in jpeg_huff_decode) */
+		JHUFF_TBL pub;
+
+		/* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of
+		 * the input data stream.	If the next Huffman code is no more
+		 * than HUFF_LOOKAHEAD bits long, we can obtain its length and
+		 * the corresponding symbol directly from these tables.
+		 */
+		int[1<<HUFF_LOOKAHEAD] look_nbits;// = new int[1<<HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */
+		byte[1<<HUFF_LOOKAHEAD] look_sym;// = new byte[1<<HUFF_LOOKAHEAD]; /* symbol, or unused */
+	}
+
+	static final class jpeg_d_coef_controller {
+		int consume_data;
+		int decompress_data;
+
+		/* Pointer to array of coefficient virtual arrays, or null if none */
+		short[][][] coef_arrays;
+
+		/* These variables keep track of the current location of the input side. */
+		/* cinfo.input_iMCU_row is also used for this. */
+		int MCU_ctr;		/* counts MCUs processed in current row */
+		int MCU_vert_offset;		/* counts MCU rows within iMCU row */
+		int MCU_rows_per_iMCU_row;	/* number of such rows needed */
+
+		/* The output side's location is represented by cinfo.output_iMCU_row. */
+
+		/* In single-pass modes, it's sufficient to buffer just one MCU.
+		 * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks,
+		 * and let the entropy decoder write into that workspace each time.
+		 * (On 80x86, the workspace is FAR even though it's not really very big;
+		 * this is to keep the module interfaces unchanged when a large coefficient
+		 * buffer is necessary.)
+		 * In multi-pass modes, this array points to the current MCU's blocks
+		 * within the virtual arrays; it is used only by the input side.
+		 */
+		short[][D_MAX_BLOCKS_IN_MCU] MCU_buffer;// = new short[D_MAX_BLOCKS_IN_MCU][];
+
+		/* In multi-pass modes, we need a virtual block array for each component. */
+		short[][][][MAX_COMPONENTS] whole_image;// = new short[MAX_COMPONENTS][][][];
+
+		/* When doing block smoothing, we latch coefficient Al values here */
+		int[] coef_bits_latch;
+
+		short[] workspace;
+
+		void start_input_pass (jpeg_decompress_struct cinfo) {
+			cinfo.input_iMCU_row = 0;
+			start_iMCU_row(cinfo);
+		}
+
+		/* Reset within-iMCU-row counters for a new row (input side) */
+		void start_iMCU_row (jpeg_decompress_struct cinfo) {
+			jpeg_d_coef_controller coef = cinfo.coef;
+
+			/* In an interleaved scan, an MCU row is the same as an iMCU row.
+			 * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows.
+			 * But at the bottom of the image, process only what's left.
+			 */
+			if (cinfo.comps_in_scan > 1) {
+				coef.MCU_rows_per_iMCU_row = 1;
+			} else {
+				if (cinfo.input_iMCU_row < (cinfo.total_iMCU_rows-1))
+					coef.MCU_rows_per_iMCU_row = cinfo.cur_comp_info[0].v_samp_factor;
+				else
+					coef.MCU_rows_per_iMCU_row = cinfo.cur_comp_info[0].last_row_height;
+			}
+
+			coef.MCU_ctr = 0;
+			coef.MCU_vert_offset = 0;
+		}
+
+	}
+
+	static abstract class jpeg_entropy_decoder {
+		abstract void start_pass (jpeg_decompress_struct cinfo);
+		abstract bool decode_mcu (jpeg_decompress_struct cinfo, short[][] MCU_data);
+
+		/* This is here to share code between baseline and progressive decoders; */
+		/* other modules probably should not use it */
+		bool insufficient_data;	/* set true after emitting warning */
+
+		bitread_working_state br_state_local;
+		savable_state state_local;
+        public this(){
+            br_state_local = new bitread_working_state();
+            state_local = new savable_state();
+        }
+	}
+
+	static final class huff_entropy_decoder : jpeg_entropy_decoder {
+		bitread_perm_state bitstate;// = new bitread_perm_state();	/* Bit buffer at start of MCU */
+		savable_state saved;// = new savable_state();		/* Other state at start of MCU */
+
+		/* These fields are NOT loaded into local working state. */
+		int restarts_to_go;	/* MCUs left in this restart interval */
+
+		/* Pointers to derived tables (these workspaces have image lifespan) */
+		d_derived_tbl[NUM_HUFF_TBLS] dc_derived_tbls;// = new d_derived_tbl[NUM_HUFF_TBLS];
+		d_derived_tbl[NUM_HUFF_TBLS] ac_derived_tbls;// = new d_derived_tbl[NUM_HUFF_TBLS];
+
+		/* Precalculated info set up by start_pass for use in decode_mcu: */
+
+		/* Pointers to derived tables to be used for each block within an MCU */
+		d_derived_tbl[D_MAX_BLOCKS_IN_MCU] dc_cur_tbls;// = new d_derived_tbl[D_MAX_BLOCKS_IN_MCU];
+		d_derived_tbl[D_MAX_BLOCKS_IN_MCU] ac_cur_tbls;// = new d_derived_tbl[D_MAX_BLOCKS_IN_MCU];
+		/* Whether we care about the DC and AC coefficient values for each block */
+		bool[D_MAX_BLOCKS_IN_MCU] dc_needed;// = new bool[D_MAX_BLOCKS_IN_MCU];
+		bool[D_MAX_BLOCKS_IN_MCU] ac_needed;// = new bool[D_MAX_BLOCKS_IN_MCU];
+
+        public this(){
+            bitstate = new bitread_perm_state(); /* Bit buffer at start of MCU */
+            saved = new savable_state();      /* Other state at start of MCU */
+        }
+
+		void start_pass (jpeg_decompress_struct cinfo) {
+			start_pass_huff_decoder(cinfo);
+		}
+
+		bool decode_mcu (jpeg_decompress_struct cinfo, short[][] MCU_data) {
+			huff_entropy_decoder entropy = this;
+			int blkn;
+//			BITREAD_STATE_VARS;
+			int get_buffer;
+			int bits_left;
+//			bitread_working_state br_state = new bitread_working_state();
+//			savable_state state = new savable_state();
+			bitread_working_state br_state = br_state_local;
+			savable_state state = state_local;
+
+				/* Process restart marker if needed; may have to suspend */
+			if (cinfo.restart_interval != 0) {
+				if (entropy.restarts_to_go == 0)
+					if (! process_restart(cinfo))
+						return false;
+			}
+
+			/* If we've run out of data, just leave the MCU set to zeroes.
+			 * This way, we return uniform gray for the remainder of the segment.
+			 */
+			if (! entropy.insufficient_data) {
+
+				/* Load up working state */
+//				BITREAD_LOAD_STATE(cinfo,entropy.bitstate);
+				br_state.cinfo = cinfo;
+				br_state.buffer = cinfo.buffer;
+				br_state.bytes_in_buffer = cinfo.bytes_in_buffer;
+				br_state.bytes_offset = cinfo.bytes_offset;
+				get_buffer = entropy.bitstate.get_buffer;
+				bits_left = entropy.bitstate.bits_left;
+
+//				ASSIGN_STATE(state, entropy.saved);
+				state.last_dc_val[0] = entropy.saved.last_dc_val[0];
+				state.last_dc_val[1] = entropy.saved.last_dc_val[1];
+				state.last_dc_val[2] = entropy.saved.last_dc_val[2];
+				state.last_dc_val[3] = entropy.saved.last_dc_val[3];
+
+				/* Outer loop handles each block in the MCU */
+
+				for (blkn = 0; blkn < cinfo.blocks_in_MCU; blkn++) {
+					short[] block = MCU_data[blkn];
+					d_derived_tbl dctbl = entropy.dc_cur_tbls[blkn];
+					d_derived_tbl actbl = entropy.ac_cur_tbls[blkn];
+					int s = 0, k, r;
+
+					/* Decode a single block's worth of coefficients */
+
+					/* Section F.2.2.1: decode the DC coefficient difference */
+//					HUFF_DECODE(s, br_state, dctbl, return FALSE, label1);
+					{
+					int nb = 0, look;
+					if (bits_left < HUFF_LOOKAHEAD) {
+						if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) {
+							return false;
+						}
+						get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						if (bits_left < HUFF_LOOKAHEAD) {
+							nb = 1;
+//							goto slowlabel;
+							if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,dctbl,nb)) < 0) {
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						}
+					}
+//					look = PEEK_BITS(HUFF_LOOKAHEAD);
+					if (nb != 1) {
+						look = (( (get_buffer >> (bits_left -	(HUFF_LOOKAHEAD)))) & ((1<<(HUFF_LOOKAHEAD))-1));
+						if ((nb = dctbl.look_nbits[look]) != 0) {
+//							DROP_BITS(nb);
+							bits_left -= nb;
+							s = dctbl.look_sym[look] & 0xFF;
+						} else {
+							nb = HUFF_LOOKAHEAD+1;
+//							slowlabel:
+							if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,dctbl,nb)) < 0) {
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						}
+					}
+					}
+
+					if (s != 0) {
+//						CHECK_BIT_BUFFER(br_state, s, return FALSE);
+						{
+						if (bits_left < (s)) {
+							if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) {
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						}
+						}
+//						r = GET_BITS(s);
+						r = (( (get_buffer >> (bits_left -= (s)))) & ((1<<(s))-1));
+//						s = HUFF_EXTEND(r, s);
+						s = ((r) < extend_test[s] ? (r) + extend_offset[s] : (r));
+					}
+
+					if (entropy.dc_needed[blkn]) {
+						/* Convert DC difference to actual value, update last_dc_val */
+						int ci = cinfo.MCU_membership[blkn];
+						s += state.last_dc_val[ci];
+						state.last_dc_val[ci] = s;
+						/* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */
+						block[0] = cast(short) s;
+					}
+
+					if (entropy.ac_needed[blkn]) {
+
+						/* Section F.2.2.2: decode the AC coefficients */
+						/* Since zeroes are skipped, output area must be cleared beforehand */
+						for (k = 1; k < DCTSIZE2; k++) {
+//							HUFF_DECODE(s, br_state, actbl, return FALSE, label2);
+							{
+							int nb = 0, look;
+							if (bits_left < HUFF_LOOKAHEAD) {
+								if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) {
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								if (bits_left < HUFF_LOOKAHEAD) {
+									nb = 1;
+//									goto slowlabel;
+									if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,actbl,nb)) < 0) {
+										return false;
+									}
+									get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								}
+							}
+							if (nb != 1) {
+//								look = PEEK_BITS(HUFF_LOOKAHEAD);
+								look = (( (get_buffer >> (bits_left -	(HUFF_LOOKAHEAD)))) & ((1<<(HUFF_LOOKAHEAD))-1));
+								if ((nb = actbl.look_nbits[look]) != 0) {
+//									DROP_BITS(nb);
+									bits_left -= (nb);
+									s = actbl.look_sym[look] & 0xFF;
+								} else {
+									nb = HUFF_LOOKAHEAD+1;
+//									slowlabel:
+									if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,actbl,nb)) < 0) {
+										return false;
+									}
+									get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								}
+							}
+							}
+							r = s >> 4;
+							s &= 15;
+
+							if (s != 0) {
+								k += r;
+//								CHECK_BIT_BUFFER(br_state, s, return FALSE);
+								{
+								if (bits_left < (s)) {
+									if (!jpeg_fill_bit_buffer(br_state, get_buffer, bits_left, s)) {
+										return false;
+									}
+									get_buffer = br_state.get_buffer;
+									bits_left = br_state.bits_left;
+								}
+								}
+//								r = GET_BITS(s);
+								r = (((get_buffer >> (bits_left -= (s)))) & ((1 << (s)) - 1));
+//								s = HUFF_EXTEND(r, s);
+								s = ((r) < extend_test[s] ? (r) + extend_offset[s] : (r));
+								/*
+								 * Output coefficient in natural (dezigzagged)
+								 * order. Note: the extra entries in
+								 * jpeg_natural_order[] will save us if k >=
+								 * DCTSIZE2, which could happen if the data is
+								 * corrupted.
+								 */
+								block[jpeg_natural_order[k]] = cast(short) s;
+							} else {
+								if (r != 15)
+									break;
+								k += 15;
+							}
+						}
+
+					} else {
+
+						/* Section F.2.2.2: decode the AC coefficients */
+						/* In this path we just discard the values */
+						for (k = 1; k < DCTSIZE2; k++) {
+//							HUFF_DECODE(s, br_state, actbl, return FALSE, label3);
+							{
+							int nb = 0, look;
+							if (bits_left < HUFF_LOOKAHEAD) {
+								if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) {
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								if (bits_left < HUFF_LOOKAHEAD) {
+									nb = 1;
+//									goto slowlabel;
+									if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,actbl,nb)) < 0) {
+										return false;
+									}
+									get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								}
+							}
+							if (nb != 1) {
+//								look = PEEK_BITS(HUFF_LOOKAHEAD);
+								look = (( (get_buffer >> (bits_left -	(HUFF_LOOKAHEAD)))) & ((1<<(HUFF_LOOKAHEAD))-1));
+								if ((nb = actbl.look_nbits[look]) != 0) {
+//									DROP_BITS(nb);
+									bits_left -= (nb);
+									s = actbl.look_sym[look] & 0xFF;
+								} else {
+									nb = HUFF_LOOKAHEAD+1;
+//									slowlabel:
+									if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,actbl,nb)) < 0) {
+										return false;
+									}
+									get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								}
+							}
+							}
+							r = s >> 4;
+							s &= 15;
+
+							if (s != 0) {
+								k += r;
+//								CHECK_BIT_BUFFER(br_state, s, return FALSE);
+								{
+								if (bits_left < (s)) {
+									if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) {
+										return false;
+									}
+									get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+									}
+								}
+//								DROP_BITS(s);
+								bits_left -= s;
+							} else {
+								if (r != 15)
+									break;
+								k += 15;
+							}
+						}
+
+					}
+				}
+
+				/* Completed MCU, so update state */
+//				BITREAD_SAVE_STATE(cinfo,entropy.bitstate);
+				cinfo.buffer = br_state.buffer;
+				cinfo.bytes_in_buffer = br_state.bytes_in_buffer;
+				cinfo.bytes_offset = br_state.bytes_offset;
+				entropy.bitstate.get_buffer = get_buffer;
+				entropy.bitstate.bits_left = bits_left;
+//				ASSIGN_STATE(entropy.saved, state);
+				entropy.saved.last_dc_val[0] = state.last_dc_val[0];
+				entropy.saved.last_dc_val[1] = state.last_dc_val[1];
+				entropy.saved.last_dc_val[2] = state.last_dc_val[2];
+				entropy.saved.last_dc_val[3] = state.last_dc_val[3];
+			}
+
+			/* Account for restart interval (no-op if not using restarts) */
+			entropy.restarts_to_go--;
+
+			return true;
+		}
+
+		void start_pass_huff_decoder (jpeg_decompress_struct cinfo) {
+			huff_entropy_decoder entropy = this;
+			int ci, blkn, dctbl, actbl;
+			jpeg_component_info compptr;
+
+			/* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG.
+			 * This ought to be an error condition, but we make it a warning because
+			 * there are some baseline files out there with all zeroes in these bytes.
+			 */
+			if (cinfo.Ss != 0 || cinfo.Se != DCTSIZE2-1 || cinfo.Ah != 0 || cinfo.Al != 0) {
+//				WARNMS(cinfo, JWRN_NOT_SEQUENTIAL);
+			}
+
+			for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+				compptr = cinfo.cur_comp_info[ci];
+				dctbl = compptr.dc_tbl_no;
+				actbl = compptr.ac_tbl_no;
+				/* Compute derived values for Huffman tables */
+				/* We may do this more than once for a table, but it's not expensive */
+				jpeg_make_d_derived_tbl(cinfo, true, dctbl, entropy.dc_derived_tbls[dctbl] = new d_derived_tbl());
+				jpeg_make_d_derived_tbl(cinfo, false, actbl, entropy.ac_derived_tbls[actbl] = new d_derived_tbl());
+				/* Initialize DC predictions to 0 */
+				entropy.saved.last_dc_val[ci] = 0;
+			}
+
+			/* Precalculate decoding info for each block in an MCU of this scan */
+			for (blkn = 0; blkn < cinfo.blocks_in_MCU; blkn++) {
+				ci = cinfo.MCU_membership[blkn];
+				compptr = cinfo.cur_comp_info[ci];
+				/* Precalculate which table to use for each block */
+				entropy.dc_cur_tbls[blkn] = entropy.dc_derived_tbls[compptr.dc_tbl_no];
+				entropy.ac_cur_tbls[blkn] = entropy.ac_derived_tbls[compptr.ac_tbl_no];
+				/* Decide whether we really care about the coefficient values */
+				if (compptr.component_needed) {
+					entropy.dc_needed[blkn] = true;
+					/* we don't need the ACs if producing a 1/8th-size image */
+					entropy.ac_needed[blkn] = (compptr.DCT_scaled_size > 1);
+				} else {
+					entropy.dc_needed[blkn] = entropy.ac_needed[blkn] = false;
+				}
+			}
+
+			/* Initialize bitread state variables */
+			entropy.bitstate.bits_left = 0;
+			entropy.bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */
+			entropy.insufficient_data = false;
+
+			/* Initialize restart counter */
+			entropy.restarts_to_go = cinfo.restart_interval;
+		}
+
+		bool process_restart (jpeg_decompress_struct cinfo) {
+			huff_entropy_decoder entropy = this;
+			int ci;
+
+			/* Throw away any unused bits remaining in bit buffer; */
+			/* include any full bytes in next_marker's count of discarded bytes */
+			cinfo.marker.discarded_bytes += entropy.bitstate.bits_left / 8;
+			entropy.bitstate.bits_left = 0;
+
+			/* Advance past the RSTn marker */
+			if (! read_restart_marker (cinfo))
+				return false;
+
+			/* Re-initialize DC predictions to 0 */
+			for (ci = 0; ci < cinfo.comps_in_scan; ci++)
+				entropy.saved.last_dc_val[ci] = 0;
+
+			/* Reset restart counter */
+			entropy.restarts_to_go = cinfo.restart_interval;
+
+			/* Reset out-of-data flag, unless read_restart_marker left us smack up
+			 * against a marker.	In that case we will end up treating the next data
+			 * segment as empty, and we can avoid producing bogus output pixels by
+			 * leaving the flag set.
+			 */
+			if (cinfo.unread_marker == 0)
+				entropy.insufficient_data = false;
+
+			return true;
+		}
+	}
+
+	static final class phuff_entropy_decoder : jpeg_entropy_decoder {
+
+		/* These fields are loaded into local variables at start of each MCU.
+		 * In case of suspension, we exit WITHOUT updating them.
+		 */
+		bitread_perm_state bitstate;// = new bitread_perm_state();	/* Bit buffer at start of MCU */
+		savable_state saved;// = new savable_state();		/* Other state at start of MCU */
+
+		/* These fields are NOT loaded into local working state. */
+		int restarts_to_go;	/* MCUs left in this restart interval */
+
+		/* Pointers to derived tables (these workspaces have image lifespan) */
+		d_derived_tbl[NUM_HUFF_TBLS] derived_tbls;// = new d_derived_tbl[NUM_HUFF_TBLS];
+
+		d_derived_tbl ac_derived_tbl; /* active table during an AC scan */
+
+		int[DCTSIZE2] newnz_pos;// = new int[DCTSIZE2];
+
+        public this(){
+            bitstate = new bitread_perm_state(); /* Bit buffer at start of MCU */
+            saved = new savable_state();      /* Other state at start of MCU */
+        }
+
+		void start_pass (jpeg_decompress_struct cinfo) {
+			start_pass_phuff_decoder(cinfo);
+		}
+
+		bool decode_mcu (jpeg_decompress_struct cinfo, short[][] MCU_data) {
+			bool is_DC_band = (cinfo.Ss == 0);
+			if (cinfo.Ah == 0) {
+				if (is_DC_band)
+					return decode_mcu_DC_first(cinfo, MCU_data);
+				else
+					return decode_mcu_AC_first(cinfo, MCU_data);
+			} else {
+				if (is_DC_band)
+					return decode_mcu_DC_refine(cinfo, MCU_data);
+				else
+					return decode_mcu_AC_refine(cinfo, MCU_data);
+			}
+		}
+
+		bool decode_mcu_DC_refine (jpeg_decompress_struct cinfo, short[][] MCU_data) {
+			phuff_entropy_decoder entropy = this;
+			int p1 = 1 << cinfo.Al;	/* 1 in the bit position being coded */
+			int blkn;
+			short[] block;
+//			BITREAD_STATE_VARS;
+			int get_buffer;
+			int bits_left;
+//			bitread_working_state br_state = new bitread_working_state();
+			bitread_working_state br_state = br_state_local;
+
+			/* Process restart marker if needed; may have to suspend */
+			if (cinfo.restart_interval != 0) {
+				if (entropy.restarts_to_go == 0)
+					if (! process_restart(cinfo))
+						return false;
+			}
+
+			/* Not worth the cycles to check insufficient_data here,
+			 * since we will not change the data anyway if we read zeroes.
+			 */
+
+			/* Load up working state */
+//			BITREAD_LOAD_STATE(cinfo,entropy.bitstate);
+			br_state.cinfo = cinfo;
+			br_state.buffer = cinfo.buffer;
+			br_state.bytes_in_buffer = cinfo.bytes_in_buffer;
+			br_state.bytes_offset = cinfo.bytes_offset;
+			get_buffer = entropy.bitstate.get_buffer;
+			bits_left = entropy.bitstate.bits_left;
+
+			/* Outer loop handles each block in the MCU */
+
+			for (blkn = 0; blkn < cinfo.blocks_in_MCU; blkn++) {
+				block = MCU_data[blkn];
+
+				/* Encoded data is simply the next bit of the two's-complement DC value */
+//				CHECK_BIT_BUFFER(br_state, 1, return FALSE);
+				{
+				if (bits_left < (1)) {
+					if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) {
+						 return false;
+					}
+					get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+				}
+				}
+//				if (GET_BITS(1))
+				if ((( (get_buffer >> (bits_left -= (1)))) & ((1<<(1))-1)) != 0)
+					block[0] |= p1;
+					/* Note: since we use |=, repeating the assignment later is safe */
+			}
+
+			/* Completed MCU, so update state */
+//			BITREAD_SAVE_STATE(cinfo,entropy.bitstate);
+			cinfo.buffer = br_state.buffer;
+			cinfo.bytes_in_buffer = br_state.bytes_in_buffer;
+			cinfo.bytes_offset = br_state.bytes_offset;
+			entropy.bitstate.get_buffer = get_buffer;
+			entropy.bitstate.bits_left = bits_left;
+
+			/* Account for restart interval (no-op if not using restarts) */
+			entropy.restarts_to_go--;
+
+			return true;
+
+		}
+
+		bool decode_mcu_AC_refine (jpeg_decompress_struct cinfo, short[][] MCU_data) {
+			phuff_entropy_decoder entropy = this;
+			int Se = cinfo.Se;
+			int p1 = 1 << cinfo.Al;	/* 1 in the bit position being coded */
+			int m1 = (-1) << cinfo.Al;	/* -1 in the bit position being coded */
+			int s = 0, k, r;
+			int EOBRUN;
+			short[] block;
+			short[] thiscoef;
+//			BITREAD_STATE_VARS;
+			int get_buffer;
+			int bits_left;
+//			bitread_working_state br_state = new bitread_working_state();
+			bitread_working_state br_state = br_state_local;
+
+			d_derived_tbl tbl;
+			int num_newnz;
+			int[] newnz_pos = entropy.newnz_pos;
+
+				/* Process restart marker if needed; may have to suspend */
+			if (cinfo.restart_interval != 0) {
+				if (entropy.restarts_to_go == 0)
+					if (! process_restart(cinfo))
+						return false;
+			}
+
+			/* If we've run out of data, don't modify the MCU.
+			 */
+			if (! entropy.insufficient_data) {
+
+				/* Load up working state */
+//				BITREAD_LOAD_STATE(cinfo,entropy.bitstate);
+				br_state.cinfo = cinfo;
+				br_state.buffer = cinfo.buffer;
+				br_state.bytes_in_buffer = cinfo.bytes_in_buffer;
+				br_state.bytes_offset = cinfo.bytes_offset;
+				get_buffer = entropy.bitstate.get_buffer;
+				bits_left = entropy.bitstate.bits_left;
+
+				EOBRUN = entropy.saved.EOBRUN; /* only part of saved state we need */
+
+				/* There is always only one block per MCU */
+				block = MCU_data[0];
+				tbl = entropy.ac_derived_tbl;
+
+				/* If we are forced to suspend, we must undo the assignments to any newly
+				 * nonzero coefficients in the block, because otherwise we'd get confused
+				 * next time about which coefficients were already nonzero.
+				 * But we need not undo addition of bits to already-nonzero coefficients;
+				 * instead, we can test the current bit to see if we already did it.
+				 */
+				num_newnz = 0;
+
+				/* initialize coefficient loop counter to start of band */
+				k = cinfo.Ss;
+
+				if (EOBRUN == 0) {
+					for (; k <= Se; k++) {
+//						HUFF_DECODE(s, br_state, tbl, goto undoit, label3);
+						{
+						int nb = 0, look;
+						if (bits_left < HUFF_LOOKAHEAD) {
+							if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) {
+//								failaction;
+								while (num_newnz > 0)
+									block[newnz_pos[--num_newnz]] = 0;
+
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							if (bits_left < HUFF_LOOKAHEAD) {
+								nb = 1;
+//								goto slowlabel;
+								if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb)) < 0) {
+//									failaction;
+									while (num_newnz > 0)
+										block[newnz_pos[--num_newnz]] = 0;
+
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							}
+						}
+						if (nb != 1) {
+//							look = PEEK_BITS(HUFF_LOOKAHEAD);
+							look = (( (get_buffer >> (bits_left -	(HUFF_LOOKAHEAD)))) & ((1<<(HUFF_LOOKAHEAD))-1));
+							if ((nb = tbl.look_nbits[look]) != 0) {
+//								DROP_BITS(nb);
+								bits_left -= nb;
+								s = tbl.look_sym[look] & 0xFF;
+							} else {
+								nb = HUFF_LOOKAHEAD+1;
+//								slowlabel:
+								if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb)) < 0) {
+//									failaction;
+									while (num_newnz > 0)
+										block[newnz_pos[--num_newnz]] = 0;
+
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							}
+						}
+						}
+						r = s >> 4;
+						s &= 15;
+						if (s != 0) {
+							if (s != 1) {		/* size of new coef should always be 1 */
+//								WARNMS(cinfo, JWRN_HUFF_BAD_CODE);
+							}
+//							CHECK_BIT_BUFFER(br_state, 1, goto undoit);
+							{
+							if (bits_left < (1)) {
+								if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) {
+//									failaction;
+									while (num_newnz > 0)
+										block[newnz_pos[--num_newnz]] = 0;
+
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								}
+							}
+//							if (GET_BITS(1))
+							if ((( (get_buffer >> (bits_left -= (1)))) & ((1<<(1))-1)) != 0)
+								s = p1;		/* newly nonzero coef is positive */
+							else
+								s = m1;		/* newly nonzero coef is negative */
+						} else {
+							if (r != 15) {
+								EOBRUN = 1 << r;	/* EOBr, run length is 2^r + appended bits */
+								if (r != 0) {
+//									CHECK_BIT_BUFFER(br_state, r, goto undoit);
+									{
+									if (bits_left < (r)) {
+										if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,r)) {
+//											failaction;
+											while (num_newnz > 0)
+												block[newnz_pos[--num_newnz]] = 0;
+
+											return false;
+										}
+										get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+									}
+									}
+//									r = GET_BITS(r);
+									r = (( (get_buffer >> (bits_left -= (r)))) & ((1<<(r))-1));
+									EOBRUN += r;
+								}
+								break;		/* rest of block is handled by EOB logic */
+							}
+							/* note s = 0 for processing ZRL */
+						}
+						/* Advance over already-nonzero coefs and r still-zero coefs,
+						 * appending correction bits to the nonzeroes.	A correction bit is 1
+						 * if the absolute value of the coefficient must be increased.
+						 */
+						do {
+							thiscoef = block;
+							int thiscoef_offset = jpeg_natural_order[k];
+							if (thiscoef[thiscoef_offset] != 0) {
+//								CHECK_BIT_BUFFER(br_state, 1, goto undoit);
+								{
+								if (bits_left < (1)) {
+									if (!jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) {
+//										failaction;
+										while (num_newnz > 0)
+											block[newnz_pos[--num_newnz]] = 0;
+
+										return false;
+									}
+									get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+								}
+								}
+//								if (GET_BITS(1)) {
+								if ((( (get_buffer >> (bits_left -= (1)))) & ((1<<(1))-1)) != 0) {
+									if ((thiscoef[thiscoef_offset] & p1) == 0) { /* do nothing if already set it */
+										if (thiscoef[thiscoef_offset] >= 0)
+											thiscoef[thiscoef_offset] += p1;
+										else
+											thiscoef[thiscoef_offset] += m1;
+									}
+								}
+							} else {
+								if (--r < 0)
+									break;		/* reached target zero coefficient */
+							}
+							k++;
+						} while (k <= Se);
+						if (s != 0) {
+							int pos = jpeg_natural_order[k];
+							/* Output newly nonzero coefficient */
+							block[pos] = cast(short) s;
+							/* Remember its position in case we have to suspend */
+							newnz_pos[num_newnz++] = pos;
+						}
+					}
+				}
+
+				if (EOBRUN > 0) {
+					/* Scan any remaining coefficient positions after the end-of-band
+					 * (the last newly nonzero coefficient, if any).	Append a correction
+					 * bit to each already-nonzero coefficient.	A correction bit is 1
+					 * if the absolute value of the coefficient must be increased.
+					 */
+					for (; k <= Se; k++) {
+						thiscoef = block;
+						int thiscoef_offset = jpeg_natural_order[k];
+						if (thiscoef[thiscoef_offset] != 0) {
+//							CHECK_BIT_BUFFER(br_state, 1, goto undoit);
+							{
+							if (bits_left < (1)) {
+								if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,1)) {
+//									failaction;
+									while (num_newnz > 0)
+										block[newnz_pos[--num_newnz]] = 0;
+
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							}
+							}
+//							if (GET_BITS(1)) {
+							if ((( (get_buffer >> (bits_left -= (1)))) & ((1<<(1))-1)) != 0) {
+								if ((thiscoef[thiscoef_offset] & p1) == 0) { /* do nothing if already changed it */
+									if (thiscoef[thiscoef_offset] >= 0)
+										thiscoef[thiscoef_offset] += p1;
+									else
+										thiscoef[thiscoef_offset] += m1;
+								}
+							}
+						}
+					}
+						/* Count one block completed in EOB run */
+					EOBRUN--;
+				}
+
+				/* Completed MCU, so update state */
+//				BITREAD_SAVE_STATE(cinfo,entropy.bitstate);
+				cinfo.buffer = br_state.buffer;
+				cinfo.bytes_in_buffer = br_state.bytes_in_buffer;
+				cinfo.bytes_offset = br_state.bytes_offset;
+				entropy.bitstate.get_buffer = get_buffer;
+				entropy.bitstate.bits_left = bits_left;
+
+				entropy.saved.EOBRUN = EOBRUN; /* only part of saved state we need */
+			}
+
+			/* Account for restart interval (no-op if not using restarts) */
+			entropy.restarts_to_go--;
+
+			return true;
+
+//			undoit:
+//				/* Re-zero any output coefficients that we made newly nonzero */
+//				while (num_newnz > 0)
+//					(*block)[newnz_pos[--num_newnz]] = 0;
+//
+//				return false;
+
+		}
+
+		bool decode_mcu_AC_first (jpeg_decompress_struct cinfo, short[][] MCU_data) {
+			phuff_entropy_decoder entropy = this;
+			int Se = cinfo.Se;
+			int Al = cinfo.Al;
+			int s = 0, k, r;
+			int EOBRUN;
+			short[] block;
+//			BITREAD_STATE_VARS;
+			int get_buffer;
+			int bits_left;
+//			bitread_working_state br_state = new bitread_working_state();
+			bitread_working_state br_state = br_state_local;
+
+			d_derived_tbl tbl;
+
+			/* Process restart marker if needed; may have to suspend */
+			if (cinfo.restart_interval != 0) {
+				if (entropy.restarts_to_go == 0)
+					if (! process_restart(cinfo))
+						return false;
+			}
+
+			/* If we've run out of data, just leave the MCU set to zeroes.
+			 * This way, we return uniform gray for the remainder of the segment.
+			 */
+			if (! entropy.insufficient_data) {
+
+				/* Load up working state.
+				 * We can avoid loading/saving bitread state if in an EOB run.
+				 */
+				EOBRUN = entropy.saved.EOBRUN;	/* only part of saved state we need */
+
+				/* There is always only one block per MCU */
+
+				if (EOBRUN > 0)		/* if it's a band of zeroes... */
+					EOBRUN--;			/* ...process it now (we do nothing) */
+				else {
+//					BITREAD_LOAD_STATE(cinfo,entropy.bitstate);
+					br_state.cinfo = cinfo;
+					br_state.buffer = cinfo.buffer;
+					br_state.bytes_in_buffer = cinfo.bytes_in_buffer;
+					br_state.bytes_offset = cinfo.bytes_offset;
+					get_buffer = entropy.bitstate.get_buffer;
+					bits_left = entropy.bitstate.bits_left;
+
+					block = MCU_data[0];
+					tbl = entropy.ac_derived_tbl;
+
+					for (k = cinfo.Ss; k <= Se; k++) {
+//						HUFF_DECODE(s, br_state, tbl, return FALSE, label2);
+						{
+						int nb = 0, look;
+						if (bits_left < HUFF_LOOKAHEAD) {
+							if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) {
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							if (bits_left < HUFF_LOOKAHEAD) {
+								nb = 1;
+//								goto slowlabel;
+								if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb)) < 0) {
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							}
+						}
+						if (nb != 1) {
+//							look = PEEK_BITS(HUFF_LOOKAHEAD);
+							look = (( (get_buffer >> (bits_left -	(HUFF_LOOKAHEAD)))) & ((1<<(HUFF_LOOKAHEAD))-1));
+
+							if ((nb = tbl.look_nbits[look]) != 0) {
+//								DROP_BITS(nb);
+								bits_left -= nb;
+								s = tbl.look_sym[look] & 0xFF;
+							} else {
+								nb = HUFF_LOOKAHEAD+1;
+//								slowlabel:
+								if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb)) < 0) {
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							}
+						}
+						}
+						r = s >> 4;
+						s &= 15;
+						if (s != 0) {
+							k += r;
+//							CHECK_BIT_BUFFER(br_state, s, return FALSE);
+							{
+							if (bits_left < (s)) {
+								if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) {
+									return false;
+								}
+								get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+							}
+							}
+//							r = GET_BITS(s);
+							r = (( (get_buffer >> (bits_left -= (s)))) & ((1<<(s))-1));
+//							s = HUFF_EXTEND(r, s);
+							s = ((r) < extend_test[s] ? (r) + extend_offset[s] : (r));
+							/* Scale and output coefficient in natural (dezigzagged) order */
+							block[jpeg_natural_order[k]] = cast(short) (s << Al);
+						} else {
+							if (r == 15) {	/* ZRL */
+								k += 15;		/* skip 15 zeroes in band */
+							} else {		/* EOBr, run length is 2^r + appended bits */
+								EOBRUN = 1 << r;
+								if (r != 0) {		/* EOBr, r > 0 */
+//									CHECK_BIT_BUFFER(br_state, r, return FALSE);
+									{
+									if (bits_left < (r)) {
+										if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,r)) {
+											return false;
+										}
+										get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+									}
+									}
+//									r = GET_BITS(r);
+									r = (( (get_buffer >> (bits_left -= (r)))) & ((1<<(r))-1));
+									EOBRUN += r;
+								}
+								EOBRUN--;		/* this band is processed at this moment */
+								break;		/* force end-of-band */
+							}
+						}
+					}
+
+//					BITREAD_SAVE_STATE(cinfo,entropy.bitstate);
+					cinfo.buffer = br_state.buffer;
+					cinfo.bytes_in_buffer = br_state.bytes_in_buffer;
+					cinfo.bytes_offset = br_state.bytes_offset;
+					entropy.bitstate.get_buffer = get_buffer;
+					entropy.bitstate.bits_left = bits_left;
+				}
+
+				/* Completed MCU, so update state */
+				entropy.saved.EOBRUN = EOBRUN;	/* only part of saved state we need */
+			}
+
+			/* Account for restart interval (no-op if not using restarts) */
+			entropy.restarts_to_go--;
+
+			return true;
+		}
+
+		bool decode_mcu_DC_first (jpeg_decompress_struct cinfo, short[][] MCU_data) {
+			phuff_entropy_decoder entropy = this;
+			int Al = cinfo.Al;
+			int s = 0, r;
+			int blkn, ci;
+			short[] block;
+//			BITREAD_STATE_VARS;
+			int get_buffer;
+			int bits_left;
+//			bitread_working_state br_state = new bitread_working_state();
+			bitread_working_state br_state = br_state_local;
+
+//			savable_state state = new savable_state();
+			savable_state state = state_local;
+			d_derived_tbl tbl;
+			jpeg_component_info compptr;
+
+			/* Process restart marker if needed; may have to suspend */
+			if (cinfo.restart_interval != 0) {
+				if (entropy.restarts_to_go == 0)
+					if (! process_restart(cinfo))
+						return false;
+			}
+
+			/* If we've run out of data, just leave the MCU set to zeroes.
+			 * This way, we return uniform gray for the remainder of the segment.
+			 */
+			if (! entropy.insufficient_data) {
+
+				/* Load up working state */
+//				BITREAD_LOAD_STATE(cinfo,entropy.bitstate);
+				br_state.cinfo = cinfo;
+				br_state.buffer = cinfo.buffer;
+				br_state.bytes_in_buffer = cinfo.bytes_in_buffer;
+				br_state.bytes_offset = cinfo.bytes_offset;
+				get_buffer = entropy.bitstate.get_buffer;
+				bits_left = entropy.bitstate.bits_left;
+
+//				ASSIGN_STATE(state, entropy.saved);
+				state.EOBRUN = entropy.saved.EOBRUN;
+				state.last_dc_val[0] = entropy.saved.last_dc_val[0];
+				state.last_dc_val[1] = entropy.saved.last_dc_val[1];
+				state.last_dc_val[2] = entropy.saved.last_dc_val[2];
+				state.last_dc_val[3] = entropy.saved.last_dc_val[3];
+
+				/* Outer loop handles each block in the MCU */
+
+				for (blkn = 0; blkn < cinfo.blocks_in_MCU; blkn++) {
+					block = MCU_data[blkn];
+					ci = cinfo.MCU_membership[blkn];
+					compptr = cinfo.cur_comp_info[ci];
+					tbl = entropy.derived_tbls[compptr.dc_tbl_no];
+
+					/* Decode a single block's worth of coefficients */
+
+					/* Section F.2.2.1: decode the DC coefficient difference */
+//					HUFF_DECODE(s, br_state, tbl, return FALSE, label1);
+					{
+					int nb = 0, look;
+					if (bits_left < HUFF_LOOKAHEAD) {
+						if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left, 0)) {
+							return false;
+						}
+						get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						if (bits_left < HUFF_LOOKAHEAD) {
+							nb = 1;
+//							goto slowlabel;
+							if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb)) < 0) {
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						}
+					}
+					if (nb != 1) {
+//						look = PEEK_BITS(HUFF_LOOKAHEAD);
+						look = (( (get_buffer >> (bits_left -	(HUFF_LOOKAHEAD)))) & ((1<<(HUFF_LOOKAHEAD))-1));
+
+						if ((nb = tbl.look_nbits[look]) != 0) {
+//							DROP_BITS(nb);
+							bits_left -= nb;
+							s = tbl.look_sym[look] & 0xFF;
+						} else {
+							nb = HUFF_LOOKAHEAD+1;
+//							slowlabel:
+							if ((s=jpeg_huff_decode(br_state,get_buffer,bits_left,tbl,nb)) < 0) {
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						}
+					}
+					}
+					if (s != 0) {
+//						CHECK_BIT_BUFFER(br_state, s, return FALSE);
+						{
+						if (bits_left < (s)) {
+							if (! jpeg_fill_bit_buffer(br_state,get_buffer,bits_left,s)) {
+								return false;
+							}
+							get_buffer = br_state.get_buffer; bits_left = br_state.bits_left;
+						}
+						}
+//						r = GET_BITS(s);
+						r = (( (get_buffer >> (bits_left -= (s)))) & ((1<<(s))-1));
+//						s = HUFF_EXTEND(r, s);
+						s = ((r) < extend_test[s] ? (r) + extend_offset[s] : (r));
+					}
+
+						/* Convert DC difference to actual value, update last_dc_val */
+					s += state.last_dc_val[ci];
+					state.last_dc_val[ci] = s;
+					/* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */
+					block[0] = cast(short) (s << Al);
+				}
+
+				/* Completed MCU, so update state */
+//				BITREAD_SAVE_STATE(cinfo,entropy.bitstate);
+				cinfo.buffer = br_state.buffer;
+				cinfo.bytes_in_buffer = br_state.bytes_in_buffer;
+				cinfo.bytes_offset = br_state.bytes_offset;
+				entropy.bitstate.get_buffer = get_buffer;
+				entropy.bitstate.bits_left = bits_left;
+//				ASSIGN_STATE(entropy.saved, state);
+				entropy.saved.EOBRUN = state.EOBRUN;
+				entropy.saved.last_dc_val[0] = state.last_dc_val[0];
+				entropy.saved.last_dc_val[1] = state.last_dc_val[1];
+				entropy.saved.last_dc_val[2] = state.last_dc_val[2];
+				entropy.saved.last_dc_val[3] = state.last_dc_val[3];
+			}
+
+			/* Account for restart interval (no-op if not using restarts) */
+			entropy.restarts_to_go--;
+
+			return true;
+		}
+
+		bool process_restart (jpeg_decompress_struct cinfo) {
+			phuff_entropy_decoder entropy = this;
+			int ci;
+
+			/* Throw away any unused bits remaining in bit buffer; */
+			/* include any full bytes in next_marker's count of discarded bytes */
+			cinfo.marker.discarded_bytes += entropy.bitstate.bits_left / 8;
+			entropy.bitstate.bits_left = 0;
+
+			/* Advance past the RSTn marker */
+			if (! read_restart_marker (cinfo))
+				return false;
+
+			/* Re-initialize DC predictions to 0 */
+			for (ci = 0; ci < cinfo.comps_in_scan; ci++)
+				entropy.saved.last_dc_val[ci] = 0;
+				/* Re-init EOB run count, too */
+			entropy.saved.EOBRUN = 0;
+
+			/* Reset restart counter */
+			entropy.restarts_to_go = cinfo.restart_interval;
+
+			/* Reset out-of-data flag, unless read_restart_marker left us smack up
+			 * against a marker.	In that case we will end up treating the next data
+			 * segment as empty, and we can avoid producing bogus output pixels by
+			 * leaving the flag set.
+			 */
+			if (cinfo.unread_marker == 0)
+				entropy.insufficient_data = false;
+
+			return true;
+		}
+
+		void start_pass_phuff_decoder (jpeg_decompress_struct cinfo) {
+			phuff_entropy_decoder entropy = this;
+			bool is_DC_band, bad;
+			int ci, coefi, tbl;
+			int[] coef_bit_ptr;
+			jpeg_component_info compptr;
+
+			is_DC_band = (cinfo.Ss == 0);
+
+			/* Validate scan parameters */
+			bad = false;
+			if (is_DC_band) {
+				if (cinfo.Se != 0)
+					bad = true;
+			} else {
+				/* need not check Ss/Se < 0 since they came from unsigned bytes */
+				if (cinfo.Ss > cinfo.Se || cinfo.Se >= DCTSIZE2)
+					bad = true;
+				/* AC scans may have only one component */
+				if (cinfo.comps_in_scan != 1)
+					bad = true;
+			}
+			if (cinfo.Ah != 0) {
+				/* Successive approximation refinement scan: must have Al = Ah-1. */
+				if (cinfo.Al != cinfo.Ah-1)
+					bad = true;
+			}
+			if (cinfo.Al > 13)		/* need not check for < 0 */
+				bad = true;
+			/* Arguably the maximum Al value should be less than 13 for 8-bit precision,
+			 * but the spec doesn't say so, and we try to be liberal about what we
+			 * accept.	Note: large Al values could result in out-of-range DC
+			 * coefficients during early scans, leading to bizarre displays due to
+			 * overflows in the IDCT math.	But we won't crash.
+			 */
+			if (bad)
+				error();
+//				ERREXIT4(cinfo, JERR_BAD_PROGRESSION, cinfo.Ss, cinfo.Se, cinfo.Ah, cinfo.Al);
+			/* Update progression status, and verify that scan order is legal.
+			 * Note that inter-scan inconsistencies are treated as warnings
+			 * not fatal errors ... not clear if this is right way to behave.
+			 */
+			for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+				int cindex = cinfo.cur_comp_info[ci].component_index;
+				coef_bit_ptr = cinfo.coef_bits[cindex];
+				if (!is_DC_band && coef_bit_ptr[0] < 0) {/* AC without prior DC scan */
+//					WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0);
+				}
+				for (coefi = cinfo.Ss; coefi <= cinfo.Se; coefi++) {
+					int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi];
+					if (cinfo.Ah != expected) {
+//						WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi);
+					}
+					coef_bit_ptr[coefi] = cinfo.Al;
+				}
+			}
+
+			/* Select MCU decoding routine */
+//			if (cinfo.Ah == 0) {
+//				if (is_DC_band)
+//					entropy.pub.decode_mcu = decode_mcu_DC_first;
+//				else
+//					entropy.pub.decode_mcu = decode_mcu_AC_first;
+//			} else {
+//				if (is_DC_band)
+//					entropy.pub.decode_mcu = decode_mcu_DC_refine;
+//				else
+//					entropy.pub.decode_mcu = decode_mcu_AC_refine;
+//			}
+
+			for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+				compptr = cinfo.cur_comp_info[ci];
+				/* Make sure requested tables are present, and compute derived tables.
+				 * We may build same derived table more than once, but it's not expensive.
+				 */
+				if (is_DC_band) {
+					if (cinfo.Ah == 0) {	/* DC refinement needs no table */
+						tbl = compptr.dc_tbl_no;
+						jpeg_make_d_derived_tbl(cinfo, true, tbl, entropy.derived_tbls[tbl] = new d_derived_tbl());
+					}
+				} else {
+					tbl = compptr.ac_tbl_no;
+					jpeg_make_d_derived_tbl(cinfo, false, tbl, entropy.derived_tbls[tbl] = new d_derived_tbl());
+					/* remember the single active table */
+					entropy.ac_derived_tbl = entropy.derived_tbls[tbl];
+				}
+				/* Initialize DC predictions to 0 */
+				entropy.saved.last_dc_val[ci] = 0;
+			}
+
+			/* Initialize bitread state variables */
+			entropy.bitstate.bits_left = 0;
+			entropy.bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */
+			entropy.insufficient_data = false;
+
+			/* Initialize private state variables */
+			entropy.saved.EOBRUN = 0;
+
+			/* Initialize restart counter */
+			entropy.restarts_to_go = cinfo.restart_interval;
+		}
+
+	}
+
+	static final class jpeg_component_info {
+		/* These values are fixed over the whole image. */
+		/* For compression, they must be supplied by parameter setup; */
+		/* for decompression, they are read from the SOF marker. */
+		int component_id;		/* identifier for this component (0..255) */
+		int component_index;		/* its index in SOF or cinfo.comp_info[] */
+		int h_samp_factor;		/* horizontal sampling factor (1..4) */
+		int v_samp_factor;		/* vertical sampling factor (1..4) */
+		int quant_tbl_no;		/* quantization table selector (0..3) */
+		/* These values may vary between scans. */
+		/* For compression, they must be supplied by parameter setup; */
+		/* for decompression, they are read from the SOS marker. */
+		/* The decompressor output side may not use these variables. */
+		int dc_tbl_no;		/* DC entropy table selector (0..3) */
+		int ac_tbl_no;		/* AC entropy table selector (0..3) */
+
+		/* Remaining fields should be treated as private by applications. */
+
+		/* These values are computed during compression or decompression startup: */
+		/* Component's size in DCT blocks.
+		 * Any dummy blocks added to complete an MCU are not counted; therefore
+		 * these values do not depend on whether a scan is interleaved or not.
+		 */
+		int width_in_blocks;
+		int height_in_blocks;
+		/* Size of a DCT block in samples.	Always DCTSIZE for compression.
+		 * For decompression this is the size of the output from one DCT block,
+		 * reflecting any scaling we choose to apply during the IDCT step.
+		 * Values of 1,2,4,8 are likely to be supported.	Note that different
+		 * components may receive different IDCT scalings.
+		 */
+		int DCT_scaled_size;
+		/* The downsampled dimensions are the component's actual, unpadded number
+		 * of samples at the main buffer (preprocessing/compression interface), thus
+		 * downsampled_width = ceil(image_width * Hi/Hmax)
+		 * and similarly for height.	For decompression, IDCT scaling is included, so
+		 * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE)
+		 */
+		int downsampled_width;	 /* actual width in samples */
+		int downsampled_height; /* actual height in samples */
+		/* This flag is used only for decompression.	In cases where some of the
+		 * components will be ignored (eg grayscale output from YCbCr image),
+		 * we can skip most computations for the unused components.
+		 */
+		bool component_needed;	/* do we need the value of this component? */
+
+		/* These values are computed before starting a scan of the component. */
+		/* The decompressor output side may not use these variables. */
+		int MCU_width;		/* number of blocks per MCU, horizontally */
+		int MCU_height;		/* number of blocks per MCU, vertically */
+		int MCU_blocks;		/* MCU_width * MCU_height */
+		int MCU_sample_width;		/* MCU width in samples, MCU_width*DCT_scaled_size */
+		int last_col_width;		/* # of non-dummy blocks across in last MCU */
+		int last_row_height;		/* # of non-dummy blocks down in last MCU */
+
+		/* Saved quantization table for component; null if none yet saved.
+		 * See jdinput.c comments about the need for this information.
+		 * This field is currently used only for decompression.
+		 */
+		JQUANT_TBL quant_table;
+
+		/* Private per-component storage for DCT or IDCT subsystem. */
+		int[] dct_table;
+	}
+
+	static final class jpeg_color_quantizer {
+//		JMETHOD(void, start_pass, (j_decompress_ptr cinfo, bool is_pre_scan));
+//		JMETHOD(void, color_quantize, (j_decompress_ptr cinfo,
+//					 JSAMPARRAY input_buf, JSAMPARRAY output_buf,
+//					 int num_rows));
+//		JMETHOD(void, finish_pass, (j_decompress_ptr cinfo));
+//		JMETHOD(void, new_color_map, (j_decompress_ptr cinfo));
+
+		/* Initially allocated colormap is saved here */
+		int[][] sv_colormap;	/* The color map as a 2-D pixel array */
+		int sv_actual;		/* number of entries in use */
+
+		int[][] colorindex;	/* Precomputed mapping for speed */
+		/* colorindex[i][j] = index of color closest to pixel value j in component i,
+		 * premultiplied as described above.	Since colormap indexes must fit into
+		 * JSAMPLEs, the entries of this array will too.
+		 */
+		bool is_padded;		/* is the colorindex padded for odither? */
+
+		int[MAX_Q_COMPS] Ncolors;// = new int [MAX_Q_COMPS];	/* # of values alloced to each component */
+
+		/* Variables for ordered dithering */
+		int row_index;		/* cur row's vertical index in dither matrix */
+//			ODITHER_MATRIX_PTR odither[MAX_Q_COMPS]; /* one dither array per component */
+
+		/* Variables for Floyd-Steinberg dithering */
+//			FSERRPTR fserrors[MAX_Q_COMPS]; /* accumulated errors */
+		bool on_odd_row;
+
+		void start_pass (jpeg_decompress_struct cinfo, bool is_pre_scan) {
+			error();
+		}
+	}
+
+	static final class jpeg_upsampler {
+//		JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+//		JMETHOD(void, upsample, (j_decompress_ptr cinfo,
+//					 JSAMPIMAGE input_buf,
+//					 JDIMENSION *in_row_group_ctr,
+//					 JDIMENSION in_row_groups_avail,
+//					 JSAMPARRAY output_buf,
+//					 JDIMENSION *out_row_ctr,
+//					 JDIMENSION out_rows_avail));
+
+		bool need_context_rows;	/* TRUE if need rows above & below */
+
+		/* Color conversion buffer.	When using separate upsampling and color
+		 * conversion steps, this buffer holds one upsampled row group until it
+		 * has been color converted and output.
+		 * Note: we do not allocate any storage for component(s) which are full-size,
+		 * ie do not need rescaling.	The corresponding entry of color_buf[] is
+		 * simply set to point to the input data array, thereby avoiding copying.
+		 */
+		byte[][][MAX_COMPONENTS] color_buf;// = new byte[MAX_COMPONENTS][][];
+		int[MAX_COMPONENTS] color_buf_offset;// = new int[MAX_COMPONENTS];
+
+		/* Per-component upsampling method pointers */
+		int[MAX_COMPONENTS] methods;// = new int[MAX_COMPONENTS];
+
+		int next_row_out;		/* counts rows emitted from color_buf */
+		int rows_to_go;	/* counts rows remaining in image */
+
+		/* Height of an input row group for each component. */
+		int[MAX_COMPONENTS] rowgroup_height;// = new int[MAX_COMPONENTS];
+
+		/* These arrays save pixel expansion factors so that int_expand need not
+		 * recompute them each time.	They are unused for other upsampling methods.
+		 */
+		byte[MAX_COMPONENTS] h_expand;// = new byte[MAX_COMPONENTS];
+		byte[MAX_COMPONENTS] v_expand;// = new byte[MAX_COMPONENTS];
+
+		void start_pass (jpeg_decompress_struct cinfo) {
+			jpeg_upsampler upsample = cinfo.upsample;
+
+			/* Mark the conversion buffer empty */
+			upsample.next_row_out = cinfo.max_v_samp_factor;
+			/* Initialize total-height counter for detecting bottom of image */
+			upsample.rows_to_go = cinfo.output_height;
+		}
+
+	}
+
+	static final class jpeg_marker_reader {
+		/* Read a restart marker --- exported for use by entropy decoder only */
+//		jpeg_marker_parser_method read_restart_marker;
+
+		/* State of marker reader --- nominally internal, but applications
+		 * supplying COM or APPn handlers might like to know the state.
+		 */
+		bool saw_SOI;		/* found SOI? */
+		bool saw_SOF;		/* found SOF? */
+		int next_restart_num;		/* next restart number expected (0-7) */
+		int discarded_bytes;	/* # of bytes skipped looking for a marker */
+
+		/* Application-overridable marker processing methods */
+//		jpeg_marker_parser_method process_COM;
+//		jpeg_marker_parser_method process_APPn[16];
+
+		/* Limit on marker data length to save for each marker type */
+		int length_limit_COM;
+		int[16] length_limit_APPn;// = new int[16];
+
+		/* Status of COM/APPn marker saving */
+//		jpeg_marker_reader cur_marker;	/* null if not processing a marker */
+//		int bytes_read;		/* data bytes read so far in marker */
+		/* Note: cur_marker is not linked into marker_list until it's all read. */
+	}
+
+
+	static final class jpeg_d_main_controller {
+//		JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode));
+		int process_data;
+
+		 /* Pointer to allocated workspace (M or M+2 row groups). */
+		byte[][][MAX_COMPONENTS] buffer;// = new byte[MAX_COMPONENTS][][];
+		int[MAX_COMPONENTS] buffer_offset;// = new int[MAX_COMPONENTS];
+
+		bool buffer_full;		/* Have we gotten an iMCU row from decoder? */
+		int[1] rowgroup_ctr;// = new int[1];	/* counts row groups output to postprocessor */
+
+		/* Remaining fields are only used in the context case. */
+
+		/* These are the master pointers to the funny-order pointer lists. */
+		byte[][][][2] xbuffer;// = new byte[2][][][];	/* pointers to weird pointer lists */
+		int[][2] xbuffer_offset;// = new int[2][];
+
+		int whichptr;			/* indicates which pointer set is now in use */
+		int context_state;		/* process_data state machine status */
+		int rowgroups_avail;	/* row groups available to postprocessor */
+		int iMCU_row_ctr;	/* counts iMCU rows to detect image top/bot */
+
+		void start_pass (jpeg_decompress_struct cinfo, int pass_mode) {
+			jpeg_d_main_controller main = cinfo.main;
+
+			switch (pass_mode) {
+				case JBUF_PASS_THRU:
+					if (cinfo.upsample.need_context_rows) {
+						main.process_data = PROCESS_DATA_CONTEXT_MAIN;
+						make_funny_pointers(cinfo); /* Create the xbuffer[] lists */
+						main.whichptr = 0;	/* Read first iMCU row into xbuffer[0] */
+						main.context_state = CTX_PREPARE_FOR_IMCU;
+						main.iMCU_row_ctr = 0;
+					} else {
+						/* Simple case with no context needed */
+						main.process_data = PROCESS_DATA_SIMPLE_MAIN;
+					}
+					main.buffer_full = false;	/* Mark buffer empty */
+					main.rowgroup_ctr[0] = 0;
+					break;
+//				#ifdef QUANT_2PASS_SUPPORTED
+//				case JBUF_CRANK_DEST:
+//					/* For last pass of 2-pass quantization, just crank the postprocessor */
+//					main.process_data = PROCESS_DATA_CRANK_POST;
+//					break;
+//				#endif
+				default:
+					error();
+//					ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+					break;
+			}
+		}
+
+	}
+
+	static final class jpeg_decomp_master {
+//		JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo));
+//		JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo));
+
+		/* State variables made visible to other modules */
+		bool is_dummy_pass;
+
+		int pass_number;		/* # of passes completed */
+
+		bool using_merged_upsample; /* true if using merged upsample/cconvert */
+
+		/* Saved references to initialized quantizer modules,
+		 * in case we need to switch modes.
+		 */
+		jpeg_color_quantizer quantizer_1pass;
+		jpeg_color_quantizer quantizer_2pass;
+	}
+
+	static final class jpeg_inverse_dct {
+//		JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+//		/* It is useful to allow each component to have a separate IDCT method. */
+//		inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS];
+		int[MAX_COMPONENTS] cur_method;// = new int[MAX_COMPONENTS];
+
+		void start_pass (jpeg_decompress_struct cinfo) {
+			jpeg_inverse_dct idct = cinfo.idct;
+			int ci, i;
+			jpeg_component_info compptr;
+			int method = 0;
+//			inverse_DCT_method_ptr method_ptr = NULL;
+			JQUANT_TBL qtbl;
+
+			for (ci = 0; ci < cinfo.num_components; ci++) {
+				compptr = cinfo.comp_info[ci];
+				/* Select the proper IDCT routine for this component's scaling */
+				switch (compptr.DCT_scaled_size) {
+//					#ifdef IDCT_SCALING_SUPPORTED
+//					case 1:
+//						method_ptr = jpeg_idct_1x1;
+//						method = JDCT_ISLOW;	/* jidctred uses islow-style table */
+//						break;
+//					case 2:
+//						method_ptr = jpeg_idct_2x2;
+//						method = JDCT_ISLOW;	/* jidctred uses islow-style table */
+//						break;
+//					case 4:
+//						method_ptr = jpeg_idct_4x4;
+//						method = JDCT_ISLOW;	/* jidctred uses islow-style table */
+//						break;
+//					#endif
+					case DCTSIZE:
+						switch (cinfo.dct_method) {
+//							#ifdef DCT_ISLOW_SUPPORTED
+							case JDCT_ISLOW:
+//								method_ptr = jpeg_idct_islow;
+								method = JDCT_ISLOW;
+								break;
+//							#endif
+//							#ifdef DCT_IFAST_SUPPORTED
+//							case JDCT_IFAST:
+//								method_ptr = jpeg_idct_ifast;
+//								method = JDCT_IFAST;
+//								break;
+//							#endif
+//							#ifdef DCT_FLOAT_SUPPORTED
+//							case JDCT_FLOAT:
+//								method_ptr = jpeg_idct_float;
+//								method = JDCT_FLOAT;
+//								break;
+//							#endif
+							default:
+								error();
+//								ERREXIT(cinfo, JERR_NOT_COMPILED);
+								break;
+						}
+						break;
+					default:
+						error();
+//						ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr.DCT_scaled_size);
+						break;
+					}
+//					idct.inverse_DCT[ci] = method_ptr;
+					/* Create multiplier table from quant table.
+					 * However, we can skip this if the component is uninteresting
+					 * or if we already built the table.	Also, if no quant table
+					 * has yet been saved for the component, we leave the
+					 * multiplier table all-zero; we'll be reading zeroes from the
+					 * coefficient controller's buffer anyway.
+					 */
+					if (! compptr.component_needed || idct.cur_method[ci] == method)
+						continue;
+					qtbl = compptr.quant_table;
+					if (qtbl == null)		/* happens if no data yet for component */
+						continue;
+					idct.cur_method[ci] = method;
+					switch (method) {
+//						#ifdef PROVIDE_ISLOW_TABLES
+						case JDCT_ISLOW:
+						{
+							/* For LL&M IDCT method, multipliers are equal to raw quantization
+							 * coefficients, but are stored as ints to ensure access efficiency.
+							 */
+							int[] ismtbl = compptr.dct_table;
+							for (i = 0; i < DCTSIZE2; i++) {
+								ismtbl[i] = qtbl.quantval[i];
+							}
+						}
+						break;
+//						#endif
+//						#ifdef DCT_IFAST_SUPPORTED
+//						case JDCT_IFAST:
+//						{
+//							/* For AA&N IDCT method, multipliers are equal to quantization
+//							 * coefficients scaled by scalefactor[row]*scalefactor[col], where
+//							 *	 scalefactor[0] = 1
+//							 *	 scalefactor[k] = cos(k*PI/16) * sqrt(2)		for k=1..7
+//							 * For integer operation, the multiplier table is to be scaled by
+//							 * IFAST_SCALE_BITS.
+//							 */
+//							int[] ifmtbl = compptr.dct_table;
+//							short aanscales[] = {
+//								/* precomputed values scaled up by 14 bits */
+//								16384, 22725, 21407, 19266, 16384, 12873,	8867,	4520,
+//								22725, 31521, 29692, 26722, 22725, 17855, 12299,	6270,
+//								21407, 29692, 27969, 25172, 21407, 16819, 11585,	5906,
+//								19266, 26722, 25172, 22654, 19266, 15137, 10426,	5315,
+//								16384, 22725, 21407, 19266, 16384, 12873,	8867,	4520,
+//								12873, 17855, 16819, 15137, 12873, 10114,	6967,	3552,
+//								8867, 12299, 11585, 10426,	8867,	6967,	4799,	2446,
+//								4520,	6270,	5906,	5315,	4520,	3552,	2446,	1247
+//							};
+//							SHIFT_TEMPS
+//
+//							for (i = 0; i < DCTSIZE2; i++) {
+//								ifmtbl[i] = DESCALE(MULTIPLY16V16( qtbl.quantval[i], aanscales[i]), CONST_BITS-IFAST_SCALE_BITS);
+//							}
+//						}
+//						break;
+//						#endif
+//						#ifdef DCT_FLOAT_SUPPORTED
+//						case JDCT_FLOAT:
+//						{
+//							/* For float AA&N IDCT method, multipliers are equal to quantization
+//							 * coefficients scaled by scalefactor[row]*scalefactor[col], where
+//							 *	 scalefactor[0] = 1
+//							 *	 scalefactor[k] = cos(k*PI/16) * sqrt(2)		for k=1..7
+//							 */
+//							FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr.dct_table;
+//							int row, col;
+//							static const double aanscalefactor[DCTSIZE] = {
+//								1.0, 1.387039845, 1.306562965, 1.175875602,
+//								1.0, 0.785694958, 0.541196100, 0.275899379
+//							};
+//
+//							i = 0;
+//							for (row = 0; row < DCTSIZE; row++) {
+//								for (col = 0; col < DCTSIZE; col++) {
+//									fmtbl[i] = (FLOAT_MULT_TYPE)
+//										((double) qtbl.quantval[i] *
+//									 aanscalefactor[row] * aanscalefactor[col]);
+//									i++;
+//								}
+//							}
+//						}
+//						break;
+//						#endif
+					default:
+						error();
+//						ERREXIT(cinfo, JERR_NOT_COMPILED);
+						break;
+				}
+			}
+		}
+	}
+
+	static final class jpeg_input_controller {
+		int consume_input;
+		bool has_multiple_scans;	/* True if file has multiple scans */
+		bool eoi_reached;
+
+		bool inheaders;		/* true until first SOS is reached */
+	}
+
+	static final class	jpeg_color_deconverter {
+//		JMETHOD(void, start_pass, (j_decompress_ptr cinfo));
+		int color_convert;
+
+		/* Private state for YCC.RGB conversion */
+		int[] Cr_r_tab;		/* => table for Cr to R conversion */
+		int[] Cb_b_tab;		/* => table for Cb to B conversion */
+		int[] Cr_g_tab;		/* => table for Cr to G conversion */
+		int[] Cb_g_tab;		/* => table for Cb to G conversion */
+
+		void start_pass (jpeg_decompress_struct cinfo) {
+			/* no work needed */
+		}
+
+	}
+
+	static final class jpeg_d_post_controller {
+//		JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode));
+		int post_process_data;
+
+		/* Color quantization source buffer: this holds output data from
+		 * the upsample/color conversion step to be passed to the quantizer.
+		 * For two-pass color quantization, we need a full-image buffer;
+		 * for one-pass operation, a strip buffer is sufficient.
+		 */
+		int[] whole_image;	/* virtual array, or NULL if one-pass */
+		int[][] buffer;		/* strip buffer, or current strip of virtual */
+		int strip_height;	/* buffer size in rows */
+		/* for two-pass mode only: */
+		int starting_row;	/* row # of first row in current strip */
+		int next_row;		/* index of next row to fill/empty in strip */
+
+		void start_pass (jpeg_decompress_struct cinfo, int pass_mode) {
+			jpeg_d_post_controller post = cinfo.post;
+
+			switch (pass_mode) {
+				case JBUF_PASS_THRU:
+					if (cinfo.quantize_colors) {
+						error(SWT.ERROR_NOT_IMPLEMENTED);
+//						/* Single-pass processing with color quantization. */
+//						post.post_process_data = POST_PROCESS_1PASS;
+//						/* We could be doing buffered-image output before starting a 2-pass
+//						 * color quantization; in that case, jinit_d_post_controller did not
+//						 * allocate a strip buffer.	Use the virtual-array buffer as workspace.
+//						 */
+//						if (post.buffer == null) {
+//							post.buffer = (*cinfo.mem.access_virt_sarray)
+//								((j_common_ptr) cinfo, post.whole_image,
+//						 		(JDIMENSION) 0, post.strip_height, TRUE);
+//						}
+					} else {
+						/* For single-pass processing without color quantization,
+						 * I have no work to do; just call the upsampler directly.
+						 */
+						post.post_process_data = POST_PROCESS_DATA_UPSAMPLE;
+					}
+					break;
+//				#ifdef QUANT_2PASS_SUPPORTED
+//				case JBUF_SAVE_AND_PASS:
+//					/* First pass of 2-pass quantization */
+//					if (post.whole_image == NULL)
+//						ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+//					post.pub.post_process_data = post_process_prepass;
+//					break;
+//				case JBUF_CRANK_DEST:
+//					/* Second pass of 2-pass quantization */
+//					if (post.whole_image == NULL)
+//						ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+//					post.pub.post_process_data = post_process_2pass;
+//					break;
+//				#endif /* QUANT_2PASS_SUPPORTED */
+					default:
+						error();
+//						ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+						break;
+			}
+			post.starting_row = post.next_row = 0;
+		}
+
+	}
+
+	static final class jpeg_decompress_struct {
+//		jpeg_error_mgr * err;	/* Error handler module */\
+//		struct jpeg_memory_mgr * mem;	/* Memory manager module */\
+//		struct jpeg_progress_mgr * progress; /* Progress monitor, or null if none */\
+//		void * client_data;		/* Available for use by application */\
+		bool is_decompressor;	/* So common code can tell which is which */
+		int global_state;		/* For checking call sequence validity */
+
+//		/* Source of compressed data */
+//		struct jpeg_source_mgr * src;
+		InputStream inputStream;
+		byte[] buffer;
+		int bytes_in_buffer;
+		int bytes_offset;
+		bool start_of_file;
+
+		/* Basic description of image --- filled in by jpeg_read_header(). */
+		/* Application may inspect these values to decide how to process image. */
+
+		int image_width;	/* nominal image width (from SOF marker) */
+		int image_height;	/* nominal image height */
+		int num_components;		/* # of color components in JPEG image */
+		int jpeg_color_space; /* colorspace of JPEG image */
+
+		/* Decompression processing parameters --- these fields must be set before
+		 * calling jpeg_start_decompress().	Note that jpeg_read_header() initializes
+		 * them to default values.
+		 */
+
+		int out_color_space; /* colorspace for output */
+
+		int scale_num, scale_denom; /* fraction by which to scale image */
+
+		double output_gamma;		/* image gamma wanted in output */
+
+		bool buffered_image;	/* true=multiple output passes */
+		bool raw_data_out;		/* true=downsampled data wanted */
+
+		int dct_method;	/* IDCT algorithm selector */
+		bool do_fancy_upsampling;	/* true=apply fancy upsampling */
+		bool do_block_smoothing;	/* true=apply interblock smoothing */
+
+		bool quantize_colors;	/* true=colormapped output wanted */
+		/* the following are ignored if not quantize_colors: */
+		int dither_mode;	/* type of color dithering to use */
+		bool two_pass_quantize;	/* true=use two-pass color quantization */
+		int desired_number_of_colors;	/* max # colors to use in created colormap */
+		/* these are significant only in buffered-image mode: */
+		bool enable_1pass_quant;	/* enable future use of 1-pass quantizer */
+		bool enable_external_quant;/* enable future use of external colormap */
+		bool enable_2pass_quant;	/* enable future use of 2-pass quantizer */
+
+		/* Description of actual output image that will be returned to application.
+		 * These fields are computed by jpeg_start_decompress().
+		 * You can also use jpeg_calc_output_dimensions() to determine these values
+		 * in advance of calling jpeg_start_decompress().
+		 */
+
+		int output_width;	/* scaled image width */
+		int output_height;	/* scaled image height */
+		int out_color_components;	/* # of color components in out_color_space */
+		int output_components;	/* # of color components returned */
+		/* output_components is 1 (a colormap index) when quantizing colors;
+		 * otherwise it equals out_color_components.
+		 */
+		int rec_outbuf_height;	/* min recommended height of scanline buffer */
+		/* If the buffer passed to jpeg_read_scanlines() is less than this many rows
+		 * high, space and time will be wasted due to unnecessary data copying.
+		 * Usually rec_outbuf_height will be 1 or 2, at most 4.
+		 */
+
+		/* When quantizing colors, the output colormap is described by these fields.
+		 * The application can supply a colormap by setting colormap non-null before
+		 * calling jpeg_start_decompress; otherwise a colormap is created during
+		 * jpeg_start_decompress or jpeg_start_output.
+		 * The map has out_color_components rows and actual_number_of_colors columns.
+		 */
+		int actual_number_of_colors;	/* number of entries in use */
+		int[] colormap;		/* The color map as a 2-D pixel array */
+
+		/* State variables: these variables indicate the progress of decompression.
+		 * The application may examine these but must not modify them.
+		 */
+
+		/* Row index of next scanline to be read from jpeg_read_scanlines().
+		 * Application may use this to control its processing loop, e.g.,
+		 * "while (output_scanline < output_height)".
+		 */
+		int output_scanline;	/* 0 .. output_height-1	*/
+
+		/* Current input scan number and number of iMCU rows completed in scan.
+		 * These indicate the progress of the decompressor input side.
+		 */
+		int input_scan_number;	/* Number of SOS markers seen so far */
+		int input_iMCU_row;	/* Number of iMCU rows completed */
+
+		/* The "output scan number" is the notional scan being displayed by the
+		 * output side.	The decompressor will not allow output scan/row number
+		 * to get ahead of input scan/row, but it can fall arbitrarily far behind.
+		 */
+		int output_scan_number;	/* Nominal scan number being displayed */
+		int output_iMCU_row;	/* Number of iMCU rows read */
+
+		/* Current progression status.	coef_bits[c][i] indicates the precision
+		 * with which component c's DCT coefficient i (in zigzag order) is known.
+		 * It is -1 when no data has yet been received, otherwise it is the point
+		 * transform (shift) value for the most recent scan of the coefficient
+		 * (thus, 0 at completion of the progression).
+		 * This pointer is null when reading a non-progressive file.
+		 */
+		int[][] coef_bits;	/* -1 or current Al value for each coef */
+
+		/* Internal JPEG parameters --- the application usually need not look at
+		 * these fields.	Note that the decompressor output side may not use
+		 * any parameters that can change between scans.
+		 */
+
+		/* Quantization and Huffman tables are carried forward across input
+		 * datastreams when processing abbreviated JPEG datastreams.
+		 */
+
+		JQUANT_TBL[NUM_QUANT_TBLS] quant_tbl_ptrs;// = new JQUANT_TBL[NUM_QUANT_TBLS];
+		/* ptrs to coefficient quantization tables, or null if not defined */
+
+		JHUFF_TBL[NUM_HUFF_TBLS] dc_huff_tbl_ptrs;// = new JHUFF_TBL[NUM_HUFF_TBLS];
+		JHUFF_TBL[NUM_HUFF_TBLS] ac_huff_tbl_ptrs;// = new JHUFF_TBL[NUM_HUFF_TBLS];
+		/* ptrs to Huffman coding tables, or null if not defined */
+
+		/* These parameters are never carried across datastreams, since they
+		 * are given in SOF/SOS markers or defined to be reset by SOI.
+		 */
+
+		int data_precision;		/* bits of precision in image data */
+
+		jpeg_component_info[] comp_info;
+		/* comp_info[i] describes component that appears i'th in SOF */
+
+		bool progressive_mode;	/* true if SOFn specifies progressive mode */
+		bool arith_code;		/* true=arithmetic coding, false=Huffman */
+
+		byte[NUM_ARITH_TBLS] arith_dc_L;// = new byte[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */
+		byte[NUM_ARITH_TBLS] arith_dc_U;// = new byte[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */
+		byte[NUM_ARITH_TBLS] arith_ac_K;// = new byte[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */
+
+		int restart_interval; /* MCUs per restart interval, or 0 for no restart */
+
+		/* These fields record data obtained from optional markers recognized by
+		 * the JPEG library.
+		 */
+		bool saw_JFIF_marker;	/* true iff a JFIF APP0 marker was found */
+		/* Data copied from JFIF marker; only valid if saw_JFIF_marker is true: */
+		byte JFIF_major_version;	/* JFIF version number */
+		byte JFIF_minor_version;
+		byte density_unit;		/* JFIF code for pixel size units */
+		short X_density;		/* Horizontal pixel density */
+		short Y_density;		/* Vertical pixel density */
+		bool saw_Adobe_marker;	/* true iff an Adobe APP14 marker was found */
+		byte Adobe_transform;	/* Color transform code from Adobe marker */
+
+		bool CCIR601_sampling;	/* true=first samples are cosited */
+
+		/* Aside from the specific data retained from APPn markers known to the
+		 * library, the uninterpreted contents of any or all APPn and COM markers
+		 * can be saved in a list for examination by the application.
+		 */
+		jpeg_marker_reader marker_list; /* Head of list of saved markers */
+
+		/* Remaining fields are known throughout decompressor, but generally
+		 * should not be touched by a surrounding application.
+		 */
+
+		/*
+		 * These fields are computed during decompression startup
+		 */
+		int max_h_samp_factor;	/* largest h_samp_factor */
+		int max_v_samp_factor;	/* largest v_samp_factor */
+
+		int min_DCT_scaled_size;	/* smallest DCT_scaled_size of any component */
+
+		int total_iMCU_rows;	/* # of iMCU rows in image */
+		/* The coefficient controller's input and output progress is measured in
+		 * units of "iMCU" (interleaved MCU) rows.	These are the same as MCU rows
+		 * in fully interleaved JPEG scans, but are used whether the scan is
+		 * interleaved or not.	We define an iMCU row as v_samp_factor DCT block
+		 * rows of each component.	Therefore, the IDCT output contains
+		 * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row.
+		 */
+
+		byte[] sample_range_limit; /* table for fast range-limiting */
+		int sample_range_limit_offset;
+
+		/*
+		 * These fields are valid during any one scan.
+		 * They describe the components and MCUs actually appearing in the scan.
+		 * Note that the decompressor output side must not use these fields.
+		 */
+		int comps_in_scan;		/* # of JPEG components in this scan */
+		jpeg_component_info[MAX_COMPS_IN_SCAN] cur_comp_info;// = new jpeg_component_info[MAX_COMPS_IN_SCAN];
+		/* *cur_comp_info[i] describes component that appears i'th in SOS */
+
+		int MCUs_per_row;	/* # of MCUs across the image */
+		int MCU_rows_in_scan;	/* # of MCU rows in the image */
+
+		int blocks_in_MCU;		/* # of DCT blocks per MCU */
+		int[D_MAX_BLOCKS_IN_MCU] MCU_membership;// = new int[D_MAX_BLOCKS_IN_MCU];
+		/* MCU_membership[i] is index in cur_comp_info of component owning */
+		/* i'th block in an MCU */
+
+		int Ss, Se, Ah, Al;		/* progressive JPEG parameters for scan */
+
+		/* This field is shared between entropy decoder and marker parser.
+		 * It is either zero or the code of a JPEG marker that has been
+		 * read from the data source, but has not yet been processed.
+		 */
+		int unread_marker;
+
+		int[DCTSIZE2] workspace;// = new int[DCTSIZE2];
+		int[1] row_ctr;// = new int[1];
+
+		/*
+		 * Links to decompression subobjects (methods, private variables of modules)
+		 */
+		jpeg_decomp_master master;
+		jpeg_d_main_controller main;
+		jpeg_d_coef_controller coef;
+		jpeg_d_post_controller post;
+		jpeg_input_controller inputctl;
+		jpeg_marker_reader marker;
+		jpeg_entropy_decoder entropy;
+		jpeg_inverse_dct idct;
+		jpeg_upsampler upsample;
+		jpeg_color_deconverter cconvert;
+		jpeg_color_quantizer cquantize;
+	}
+
+static void error() {
+	SWT.error(SWT.ERROR_INVALID_IMAGE);
+}
+
+static void error(int code) {
+	SWT.error(code);
+}
+
+static void error(char[] msg) {
+	SWT.error(SWT.ERROR_INVALID_IMAGE, null, msg);
+}
+
+static void jinit_marker_reader (jpeg_decompress_struct cinfo) {
+	jpeg_marker_reader marker = cinfo.marker = new jpeg_marker_reader();
+//	int i;
+
+	/* Initialize COM/APPn processing.
+	 * By default, we examine and then discard APP0 and APP14,
+	 * but simply discard COM and all other APPn.
+	 */
+//	marker.process_COM = skip_variable;
+	marker.length_limit_COM = 0;
+//	for (i = 0; i < 16; i++) {
+//		marker.process_APPn[i] = skip_variable;
+//		marker.length_limit_APPn[i] = 0;
+//	}
+//	marker.process_APPn[0] = get_interesting_appn;
+//	marker.process_APPn[14] = get_interesting_appn;
+	/* Reset marker processing state */
+	reset_marker_reader(cinfo);
+}
+
+static void jinit_d_coef_controller (jpeg_decompress_struct cinfo, bool need_full_buffer) {
+	jpeg_d_coef_controller coef = new jpeg_d_coef_controller();
+	cinfo.coef = coef;
+//	coef.pub.start_input_pass = start_input_pass;
+//	coef.pub.start_output_pass = start_output_pass;
+	coef.coef_bits_latch = null;
+
+	/* Create the coefficient buffer. */
+	if (need_full_buffer) {
+//#ifdef D_MULTISCAN_FILES_SUPPORTED
+		/* Allocate a full-image virtual array for each component, */
+		/* padded to a multiple of samp_factor DCT blocks in each direction. */
+		/* Note we ask for a pre-zeroed array. */
+		int ci, access_rows;
+		jpeg_component_info compptr;
+
+		for (ci = 0; ci < cinfo.num_components; ci++) {
+			compptr = cinfo.comp_info[ci];
+			access_rows = compptr.v_samp_factor;
+//#ifdef BLOCK_SMOOTHING_SUPPORTED
+			/* If block smoothing could be used, need a bigger window */
+			if (cinfo.progressive_mode)
+				access_rows *= 3;
+//#endif
+			coef.whole_image[ci] =
+				new short[][][](
+                    cast(int)jround_up( compptr.height_in_blocks, compptr.v_samp_factor),
+                    cast(int)jround_up( compptr.width_in_blocks, compptr.h_samp_factor),
+                    DCTSIZE2
+				    );
+		}
+//		coef.consume_data = consume_data;
+		coef.decompress_data = DECOMPRESS_DATA;
+		coef.coef_arrays = coef.whole_image[0]; /* link to virtual arrays */
+//		#else
+//				ERREXIT(cinfo, JERR_NOT_COMPILED);
+//		#endif
+	} else {
+		/* We only need a single-MCU buffer. */
+        foreach( inout el; coef.MCU_buffer ){
+            el = new short[](DCTSIZE2);
+        }
+//		coef.consume_data = dummy_consume_data;
+		coef.decompress_data = DECOMPRESS_ONEPASS;
+		coef.coef_arrays = null; /* flag for no virtual arrays */
+	}
+}
+
+static void start_output_pass (jpeg_decompress_struct cinfo) {
+//#ifdef BLOCK_SMOOTHING_SUPPORTED
+	jpeg_d_coef_controller coef = cinfo.coef;
+
+	/* If multipass, check to see whether to use block smoothing on this pass */
+	if (coef.coef_arrays != null) {
+		if (cinfo.do_block_smoothing && smoothing_ok(cinfo))
+			coef.decompress_data = DECOMPRESS_SMOOTH_DATA;
+		else
+			coef.decompress_data = DECOMPRESS_DATA;
+	}
+//#endif
+	cinfo.output_iMCU_row = 0;
+}
+
+static void jpeg_create_decompress(jpeg_decompress_struct cinfo) {
+	cinfo.is_decompressor = true;
+
+
+	/* Initialize marker processor so application can override methods
+	 * for COM, APPn markers before calling jpeg_read_header.
+	 */
+	cinfo.marker_list = null;
+	jinit_marker_reader(cinfo);
+
+	/* And initialize the overall input controller. */
+	jinit_input_controller(cinfo);
+
+	/* OK, I'm ready */
+	cinfo.global_state = DSTATE_START;
+}
+
+static void jpeg_calc_output_dimensions (jpeg_decompress_struct cinfo)
+/* Do computations that are needed before master selection phase */
+{
+//#ifdef IDCT_SCALING_SUPPORTED
+//	int ci;
+//	jpeg_component_info compptr;
+//#endif
+
+	/* Prevent application from calling me at wrong times */
+	if (cinfo.global_state != DSTATE_READY)
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+
+//#ifdef IDCT_SCALING_SUPPORTED
+//
+//	/* Compute actual output image dimensions and DCT scaling choices. */
+//	if (cinfo.scale_num * 8 <= cinfo.scale_denom) {
+//		/* Provide 1/8 scaling */
+//		cinfo.output_width = cast(int)
+//			jdiv_round_up(cinfo.image_width, 8L);
+//		cinfo.output_height = cast(int)
+//			jdiv_round_up(cinfo.image_height, 8L);
+//		cinfo.min_DCT_scaled_size = 1;
+//	} else if (cinfo.scale_num * 4 <= cinfo.scale_denom) {
+//		/* Provide 1/4 scaling */
+//		cinfo.output_width = cast(int)
+//			jdiv_round_up(cinfo.image_width, 4L);
+//		cinfo.output_height = cast(int)
+//			jdiv_round_up(cinfo.image_height, 4L);
+//		cinfo.min_DCT_scaled_size = 2;
+//	} else if (cinfo.scale_num * 2 <= cinfo.scale_denom) {
+//		/* Provide 1/2 scaling */
+//		cinfo.output_width = cast(int)
+//			jdiv_round_up(cinfo.image_width, 2L);
+//		cinfo.output_height = cast(int)
+//			jdiv_round_up(cinfo.image_height, 2L);
+//		cinfo.min_DCT_scaled_size = 4;
+//	} else {
+//		/* Provide 1/1 scaling */
+//		cinfo.output_width = cinfo.image_width;
+//		cinfo.output_height = cinfo.image_height;
+//		cinfo.min_DCT_scaled_size = DCTSIZE;
+//	}
+//	/* In selecting the actual DCT scaling for each component, we try to
+//	 * scale up the chroma components via IDCT scaling rather than upsampling.
+//	 * This saves time if the upsampler gets to use 1:1 scaling.
+//	 * Note this code assumes that the supported DCT scalings are powers of 2.
+//	 */
+//	for (ci = 0; ci < cinfo.num_components; ci++) {
+//		compptr = cinfo.comp_info[ci];
+//		int ssize = cinfo.min_DCT_scaled_size;
+//		while (ssize < DCTSIZE &&
+//			(compptr.h_samp_factor * ssize * 2 <= cinfo.max_h_samp_factor * cinfo.min_DCT_scaled_size) &&
+//			(compptr.v_samp_factor * ssize * 2 <= cinfo.max_v_samp_factor * cinfo.min_DCT_scaled_size))
+//		{
+//			ssize = ssize * 2;
+//		}
+//		compptr.DCT_scaled_size = ssize;
+//	}
+//
+//	/* Recompute downsampled dimensions of components;
+//	 * application needs to know these if using raw downsampled data.
+//	 */
+//	for (ci = 0; ci < cinfo.num_components; ci++) {
+//		compptr = cinfo.comp_info[ci];
+//		/* Size in samples, after IDCT scaling */
+//		compptr.downsampled_width = cast(int)
+//			jdiv_round_up(cast(long) cinfo.image_width * cast(long) (compptr.h_samp_factor * compptr.DCT_scaled_size),
+//				(cinfo.max_h_samp_factor * DCTSIZE));
+//		compptr.downsampled_height = cast(int)
+//			jdiv_round_up(cast(long) cinfo.image_height * cast(long) (compptr.v_samp_factor * compptr.DCT_scaled_size),
+//				(cinfo.max_v_samp_factor * DCTSIZE));
+//	}
+//
+//#else /* !IDCT_SCALING_SUPPORTED */
+
+	/* Hardwire it to "no scaling" */
+	cinfo.output_width = cinfo.image_width;
+	cinfo.output_height = cinfo.image_height;
+	/* jdinput.c has already initialized DCT_scaled_size to DCTSIZE,
+	 * and has computed unscaled downsampled_width and downsampled_height.
+	 */
+
+//#endif /* IDCT_SCALING_SUPPORTED */
+
+	/* Report number of components in selected colorspace. */
+	/* Probably this should be in the color conversion module... */
+	switch (cinfo.out_color_space) {
+		case JCS_GRAYSCALE:
+			cinfo.out_color_components = 1;
+			break;
+		case JCS_RGB:
+			if (RGB_PIXELSIZE != 3) {
+				cinfo.out_color_components = RGB_PIXELSIZE;
+				break;
+			}
+			//FALLTHROUGH
+		case JCS_YCbCr:
+			cinfo.out_color_components = 3;
+			break;
+		case JCS_CMYK:
+		case JCS_YCCK:
+			cinfo.out_color_components = 4;
+			break;
+		default:			/* else must be same colorspace as in file */
+			cinfo.out_color_components = cinfo.num_components;
+			break;
+	}
+	cinfo.output_components = (cinfo.quantize_colors ? 1 : cinfo.out_color_components);
+
+	/* See if upsampler will want to emit more than one row at a time */
+	if (use_merged_upsample(cinfo))
+		cinfo.rec_outbuf_height = cinfo.max_v_samp_factor;
+	else
+		cinfo.rec_outbuf_height = 1;
+}
+
+static bool use_merged_upsample (jpeg_decompress_struct cinfo) {
+//#ifdef UPSAMPLE_MERGING_SUPPORTED
+	/* Merging is the equivalent of plain box-filter upsampling */
+	if (cinfo.do_fancy_upsampling || cinfo.CCIR601_sampling)
+		return false;
+	/* jdmerge.c only supports YCC=>RGB color conversion */
+	if (cinfo.jpeg_color_space != JCS_YCbCr || cinfo.num_components != 3 ||
+			cinfo.out_color_space != JCS_RGB ||
+			cinfo.out_color_components != RGB_PIXELSIZE)
+		return false;
+	/* and it only handles 2h1v or 2h2v sampling ratios */
+	if (cinfo.comp_info[0].h_samp_factor != 2 ||
+			cinfo.comp_info[1].h_samp_factor != 1 ||
+			cinfo.comp_info[2].h_samp_factor != 1 ||
+			cinfo.comp_info[0].v_samp_factor >	2 ||
+			cinfo.comp_info[1].v_samp_factor != 1 ||
+			cinfo.comp_info[2].v_samp_factor != 1)
+		return false;
+	/* furthermore, it doesn't work if we've scaled the IDCTs differently */
+	if (cinfo.comp_info[0].DCT_scaled_size != cinfo.min_DCT_scaled_size ||
+			cinfo.comp_info[1].DCT_scaled_size != cinfo.min_DCT_scaled_size ||
+			cinfo.comp_info[2].DCT_scaled_size != cinfo.min_DCT_scaled_size)
+		return false;
+	/* ??? also need to test for upsample-time rescaling, when & if supported */
+	return true;			/* by golly, it'll work... */
+//#else
+//	return false;
+//#endif
+}
+
+static void prepare_range_limit_table (jpeg_decompress_struct cinfo)
+/* Allocate and fill in the sample_range_limit table */
+{
+	byte[] table;
+	int i;
+
+	table = new byte[5 * (MAXJSAMPLE+1) + CENTERJSAMPLE];
+	int offset = (MAXJSAMPLE+1);	/* allow negative subscripts of simple table */
+	cinfo.sample_range_limit_offset = offset;
+	cinfo.sample_range_limit = table;
+	/* First segment of "simple" table: limit[x] = 0 for x < 0 */
+	/* Main part of "simple" table: limit[x] = x */
+	for (i = 0; i <= MAXJSAMPLE; i++)
+		table[i + offset] = cast(byte)i;
+	offset += CENTERJSAMPLE;	/* Point to where post-IDCT table starts */
+	/* End of simple table, rest of first half of post-IDCT table */
+	for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++)
+		table[i+offset] = cast(byte)MAXJSAMPLE;
+	/* Second half of post-IDCT table */
+    table[offset + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE) .. offset + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE) + CENTERJSAMPLE] =
+        cinfo.sample_range_limit[ cinfo.sample_range_limit_offset .. cinfo.sample_range_limit_offset +CENTERJSAMPLE ];
+}
+
+static void build_ycc_rgb_table (jpeg_decompress_struct cinfo) {
+	jpeg_color_deconverter cconvert = cinfo.cconvert;
+	int i;
+	int x;
+//	SHIFT_TEMPS
+
+	cconvert.Cr_r_tab = new int[MAXJSAMPLE+1];
+	cconvert.Cb_b_tab = new int[MAXJSAMPLE+1];
+	cconvert.Cr_g_tab = new int[MAXJSAMPLE+1];
+	cconvert.Cb_g_tab = new int[MAXJSAMPLE+1];
+
+	for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
+		/* i is the actual input pixel value, in the range 0..MAXJSAMPLE */
+		/* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */
+		/* Cr=>R value is nearest int to 1.40200 * x */
+		cconvert.Cr_r_tab[i] = (cast(int)(1.40200f * (1<<SCALEBITS) + 0.5f) * x + ONE_HALF) >> SCALEBITS;
+		/* Cb=>B value is nearest int to 1.77200 * x */
+		cconvert.Cb_b_tab[i] = (cast(int)(1.77200f * (1<<SCALEBITS) + 0.5f) * x + ONE_HALF) >> SCALEBITS;
+		/* Cr=>G value is scaled-up -0.71414 * x */
+		cconvert.Cr_g_tab[i] = (cast(int)(- (0.71414f * (1<<SCALEBITS) + 0.5f)) * x);
+		/* Cb=>G value is scaled-up -0.34414 * x */
+		/* We also add in ONE_HALF so that need not do it in inner loop */
+		cconvert.Cb_g_tab[i] = (cast(int)(- (0.34414f* (1<<SCALEBITS) + 0.5f)) * x + ONE_HALF);
+	}
+}
+
+static void jinit_color_deconverter (jpeg_decompress_struct cinfo) {
+	jpeg_color_deconverter cconvert = cinfo.cconvert = new jpeg_color_deconverter();
+//	cconvert.start_pass = start_pass_dcolor;
+
+	/* Make sure num_components agrees with jpeg_color_space */
+	switch (cinfo.jpeg_color_space) {
+		case JCS_GRAYSCALE:
+			if (cinfo.num_components != 1)
+				error();
+//				ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+			break;
+
+		case JCS_RGB:
+		case JCS_YCbCr:
+			if (cinfo.num_components != 3)
+				error();
+//				ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+			break;
+
+		case JCS_CMYK:
+		case JCS_YCCK:
+			if (cinfo.num_components != 4)
+				error();
+//				ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+			break;
+
+		default:			/* JCS_UNKNOWN can be anything */
+			if (cinfo.num_components < 1)
+				error();
+//				ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+			break;
+	}
+
+	/* Set out_color_components and conversion method based on requested space.
+	 * Also clear the component_needed flags for any unused components,
+	 * so that earlier pipeline stages can avoid useless computation.
+	 */
+
+	int ci;
+	switch (cinfo.out_color_space) {
+		case JCS_GRAYSCALE:
+			cinfo.out_color_components = 1;
+			if (cinfo.jpeg_color_space == JCS_GRAYSCALE || cinfo.jpeg_color_space == JCS_YCbCr) {
+				cconvert.color_convert = GRAYSCALE_CONVERT;
+				/* For color.grayscale conversion, only the Y (0) component is needed */
+				for (ci = 1; ci < cinfo.num_components; ci++)
+					cinfo.comp_info[ci].component_needed = false;
+			} else
+				error();
+//				ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+			break;
+
+		case JCS_RGB:
+			cinfo.out_color_components = RGB_PIXELSIZE;
+			if (cinfo.jpeg_color_space == JCS_YCbCr) {
+				cconvert.color_convert = YCC_RGB_CONVERT;
+				build_ycc_rgb_table(cinfo);
+			} else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+				cconvert.color_convert = GRAY_RGB_CONVERT;
+			} else if (cinfo.jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) {
+				cconvert.color_convert = NULL_CONVERT;
+			} else
+				error();
+//				ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+				break;
+
+		case JCS_CMYK:
+			cinfo.out_color_components = 4;
+			if (cinfo.jpeg_color_space == JCS_YCCK) {
+				cconvert.color_convert = YCCK_CMYK_CONVERT;
+				build_ycc_rgb_table(cinfo);
+			} else if (cinfo.jpeg_color_space == JCS_CMYK) {
+				cconvert.color_convert = NULL_CONVERT;
+			} else
+				error();
+//				ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+			break;
+
+		default:
+			/* Permit null conversion to same output space */
+			if (cinfo.out_color_space == cinfo.jpeg_color_space) {
+				cinfo.out_color_components = cinfo.num_components;
+				cconvert.color_convert = NULL_CONVERT;
+			} else	/* unsupported non-null conversion */
+				error();
+//				ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+			break;
+	}
+
+	if (cinfo.quantize_colors)
+		cinfo.output_components = 1; /* single colormapped output component */
+	else
+		cinfo.output_components = cinfo.out_color_components;
+}
+
+static void jinit_d_post_controller (jpeg_decompress_struct cinfo, bool need_full_buffer) {
+	jpeg_d_post_controller post = cinfo.post = new jpeg_d_post_controller();
+//	post.pub.start_pass = start_pass_dpost;
+	post.whole_image = null;	/* flag for no virtual arrays */
+	post.buffer = null;		/* flag for no strip buffer */
+
+	/* Create the quantization buffer, if needed */
+	if (cinfo.quantize_colors) {
+		error(SWT.ERROR_NOT_IMPLEMENTED);
+//		/* The buffer strip height is max_v_samp_factor, which is typically
+//		 * an efficient number of rows for upsampling to return.
+//		 * (In the presence of output rescaling, we might want to be smarter?)
+//		 */
+//		post.strip_height = cinfo.max_v_samp_factor;
+//		if (need_full_buffer) {
+//			/* Two-pass color quantization: need full-image storage. */
+//			/* We round up the number of rows to a multiple of the strip height. */
+//#ifdef QUANT_2PASS_SUPPORTED
+//			post.whole_image = (*cinfo.mem.request_virt_sarray)
+//				((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
+//	 			cinfo.output_width * cinfo.out_color_components,
+//	 			(JDIMENSION) jround_up(cast(long) cinfo.output_height,
+//				cast(long) post.strip_height),
+//	 post.strip_height);
+//#else
+//			ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+//#endif /* QUANT_2PASS_SUPPORTED */
+//		} else {
+//			/* One-pass color quantization: just make a strip buffer. */
+//			post.buffer = (*cinfo.mem.alloc_sarray)
+//				((j_common_ptr) cinfo, JPOOL_IMAGE,
+//	 			cinfo.output_width * cinfo.out_color_components,
+//	 			post.strip_height);
+//		}
+	}
+}
+
+static void make_funny_pointers (jpeg_decompress_struct cinfo)
+/* Create the funny pointer lists discussed in the comments above.
+ * The actual workspace is already allocated (in main.buffer),
+ * and the space for the pointer lists is allocated too.
+ * This routine just fills in the curiously ordered lists.
+ * This will be repeated at the beginning of each pass.
+ */
+{
+	jpeg_d_main_controller main = cinfo.main;
+	int ci, i, rgroup;
+	int M = cinfo.min_DCT_scaled_size;
+	jpeg_component_info compptr;
+	byte[][] buf, xbuf0, xbuf1;
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		rgroup = (compptr.v_samp_factor * compptr.DCT_scaled_size) /
+			cinfo.min_DCT_scaled_size; /* height of a row group of component */
+		xbuf0 = main.xbuffer[0][ci];
+		int xbuf0_offset = main.xbuffer_offset[0][ci];
+		xbuf1 = main.xbuffer[1][ci];
+		int xbuf1_offset = main.xbuffer_offset[1][ci];
+		/* First copy the workspace pointers as-is */
+		buf = main.buffer[ci];
+		for (i = 0; i < rgroup * (M + 2); i++) {
+			xbuf0[i + xbuf0_offset] = xbuf1[i + xbuf1_offset] = buf[i];
+		}
+		/* In the second list, put the last four row groups in swapped order */
+		for (i = 0; i < rgroup * 2; i++) {
+			xbuf1[rgroup*(M-2) + i + xbuf1_offset] = buf[rgroup*M + i];
+			xbuf1[rgroup*M + i + xbuf1_offset] = buf[rgroup*(M-2) + i];
+		}
+		/* The wraparound pointers at top and bottom will be filled later
+		 * (see set_wraparound_pointers, below).	Initially we want the "above"
+		 * pointers to duplicate the first actual data line.	This only needs
+		 * to happen in xbuffer[0].
+		 */
+		for (i = 0; i < rgroup; i++) {
+			xbuf0[i - rgroup + xbuf0_offset] = xbuf0[0 + xbuf0_offset];
+		}
+	}
+}
+
+static void alloc_funny_pointers (jpeg_decompress_struct cinfo)
+/* Allocate space for the funny pointer lists.
+ * This is done only once, not once per pass.
+ */
+{
+	jpeg_d_main_controller main = cinfo.main;
+	int ci, rgroup;
+	int M = cinfo.min_DCT_scaled_size;
+	jpeg_component_info compptr;
+	byte[][] xbuf;
+
+	/* Get top-level space for component array pointers.
+	 * We alloc both arrays with one call to save a few cycles.
+	 */
+	main.xbuffer[0] = new byte[][][](cinfo.num_components);
+	main.xbuffer[1] = new byte[][][](cinfo.num_components);
+	main.xbuffer_offset[0] = new int[](cinfo.num_components);
+	main.xbuffer_offset[1] = new int[](cinfo.num_components);
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		rgroup = (compptr.v_samp_factor * compptr.DCT_scaled_size) / cinfo.min_DCT_scaled_size; /* height of a row group of component */
+		/* Get space for pointer lists --- M+4 row groups in each list.
+		 * We alloc both pointer lists with one call to save a few cycles.
+		 */
+		xbuf = new byte[][](2 * (rgroup * (M + 4)));
+		int offset = rgroup;
+		main.xbuffer_offset[0][ci] = offset;
+		main.xbuffer[0][ci] = xbuf;
+		offset += rgroup * (M + 4);
+		main.xbuffer_offset[1][ci] = offset;
+		main.xbuffer[1][ci] = xbuf;
+	}
+}
+
+
+static void jinit_d_main_controller (jpeg_decompress_struct cinfo, bool need_full_buffer) {
+	int ci, rgroup, ngroups;
+	jpeg_component_info compptr;
+
+	jpeg_d_main_controller main = cinfo.main = new jpeg_d_main_controller();
+//	main.pub.start_pass = start_pass_main;
+
+	if (need_full_buffer)		/* shouldn't happen */
+		error();
+//		ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
+
+	/* Allocate the workspace.
+	 * ngroups is the number of row groups we need.
+	 */
+	if (cinfo.upsample.need_context_rows) {
+		if (cinfo.min_DCT_scaled_size < 2) /* unsupported, see comments above */
+			error();
+//			ERREXIT(cinfo, JERR_NOTIMPL);
+		alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */
+		ngroups = cinfo.min_DCT_scaled_size + 2;
+	} else {
+		ngroups = cinfo.min_DCT_scaled_size;
+	}
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		rgroup = (compptr.v_samp_factor * compptr.DCT_scaled_size) / cinfo.min_DCT_scaled_size; /* height of a row group of component */
+		main.buffer[ci] = new byte[][]( rgroup * ngroups, compptr.width_in_blocks * compptr.DCT_scaled_size );
+	}
+}
+
+static long jround_up (long a, long b)
+/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */
+/* Assumes a >= 0, b > 0 */
+{
+	a += b - 1L;
+	return a - (a % b);
+}
+
+static void jinit_upsampler (jpeg_decompress_struct cinfo) {
+	int ci;
+	jpeg_component_info compptr;
+	bool need_buffer, do_fancy;
+	int h_in_group, v_in_group, h_out_group, v_out_group;
+
+	jpeg_upsampler upsample = new jpeg_upsampler();
+	cinfo.upsample = upsample;
+//	upsample.start_pass = start_pass_upsample;
+//	upsample.upsample = sep_upsample;
+	upsample.need_context_rows = false; /* until we find out differently */
+
+	if (cinfo.CCIR601_sampling)	/* this isn't supported */
+		error();
+//		ERREXIT(cinfo, JERR_CCIR601_NOTIMPL);
+
+	/* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1,
+	 * so don't ask for it.
+	 */
+	do_fancy = cinfo.do_fancy_upsampling && cinfo.min_DCT_scaled_size > 1;
+
+	/* Verify we can handle the sampling factors, select per-component methods,
+	 * and create storage as needed.
+	 */
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		/* Compute size of an "input group" after IDCT scaling.	This many samples
+		 * are to be converted to max_h_samp_factor * max_v_samp_factor pixels.
+		 */
+		h_in_group = (compptr.h_samp_factor * compptr.DCT_scaled_size) /
+		 cinfo.min_DCT_scaled_size;
+		v_in_group = (compptr.v_samp_factor * compptr.DCT_scaled_size) /
+		 cinfo.min_DCT_scaled_size;
+		h_out_group = cinfo.max_h_samp_factor;
+		v_out_group = cinfo.max_v_samp_factor;
+		upsample.rowgroup_height[ci] = v_in_group; /* save for use later */
+		need_buffer = true;
+		if (! compptr.component_needed) {
+			/* Don't bother to upsample an uninteresting component. */
+			upsample.methods[ci] = NOOP_UPSAMPLE;
+			need_buffer = false;
+		} else if (h_in_group == h_out_group && v_in_group == v_out_group) {
+			/* Fullsize components can be processed without any work. */
+			upsample.methods[ci] = FULLSIZE_UPSAMPLE;
+			need_buffer = false;
+		} else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) {
+			/* Special cases for 2h1v upsampling */
+			if (do_fancy && compptr.downsampled_width > 2)
+				upsample.methods[ci] = H2V1_FANCY_UPSAMPLE;
+			else
+				upsample.methods[ci] = H2V1_UPSAMPLE;
+		} else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) {
+			/* Special cases for 2h2v upsampling */
+			if (do_fancy && compptr.downsampled_width > 2) {
+				upsample.methods[ci] = H2V2_FANCY_UPSAMPLE;
+				upsample.need_context_rows = true;
+			} else
+				upsample.methods[ci] = H2V2_UPSAMPLE;
+		} else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) {
+			/* Generic integral-factors upsampling method */
+			upsample.methods[ci] = INT_UPSAMPLE;
+			upsample.h_expand[ci] = cast(byte) (h_out_group / h_in_group);
+			upsample.v_expand[ci] = cast(byte) (v_out_group / v_in_group);
+		} else
+			error();
+//			ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL);
+		if (need_buffer) {
+			upsample.color_buf[ci] = new byte[][]( cinfo.max_v_samp_factor,
+						 cast(int) jround_up(cinfo.output_width, cinfo.max_h_samp_factor));
+		}
+	}
+}
+
+static void jinit_phuff_decoder (jpeg_decompress_struct cinfo) {
+	int[][] coef_bit_ptr;
+	int ci, i;
+
+	cinfo.entropy = new phuff_entropy_decoder();
+//	entropy.pub.start_pass = start_pass_phuff_decoder;
+
+	/* Create progression status table */
+	cinfo.coef_bits = new int[][]( cinfo.num_components, DCTSIZE2 );
+	coef_bit_ptr = cinfo.coef_bits;
+	for (ci = 0; ci < cinfo.num_components; ci++)
+		for (i = 0; i < DCTSIZE2; i++)
+			coef_bit_ptr[ci][i] = -1;
+}
+
+
+static void jinit_huff_decoder (jpeg_decompress_struct cinfo) {
+
+	cinfo.entropy = new huff_entropy_decoder();
+//	entropy.pub.start_pass = start_pass_huff_decoder;
+//	entropy.pub.decode_mcu = decode_mcu;
+
+}
+
+static void jinit_inverse_dct (jpeg_decompress_struct cinfo) {
+	int ci;
+	jpeg_component_info compptr;
+
+	jpeg_inverse_dct idct = cinfo.idct = new jpeg_inverse_dct();
+//	idct.pub.start_pass = start_pass;
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		/* Allocate and pre-zero a multiplier table for each component */
+		compptr.dct_table = new int[DCTSIZE2];
+		/* Mark multiplier table not yet set up for any method */
+		idct.cur_method[ci] = -1;
+	}
+}
+
+static final int CONST_BITS = 13;
+static final int PASS1_BITS = 2;
+static final int RANGE_MASK =(MAXJSAMPLE * 4 + 3);
+static void jpeg_idct_islow (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	short[] coef_block,
+	byte[][] output_buf, int output_buf_offset, int output_col)
+{
+	int tmp0, tmp1, tmp2, tmp3;
+	int tmp10, tmp11, tmp12, tmp13;
+	int z1, z2, z3, z4, z5;
+	short[] inptr;
+	int[] quantptr;
+	int[] wsptr;
+	byte[] outptr;
+	byte[] range_limit = cinfo.sample_range_limit;
+	int range_limit_offset = cinfo.sample_range_limit_offset + CENTERJSAMPLE;
+	int ctr;
+	int[] workspace = cinfo.workspace;	/* buffers data between passes */
+//	SHIFT_TEMPS
+
+	/* Pass 1: process columns from input, store into work array. */
+	/* Note results are scaled up by sqrt(8) compared to a true IDCT; */
+	/* furthermore, we scale the results by 2**PASS1_BITS. */
+
+	inptr = coef_block;
+	quantptr = compptr.dct_table;
+	wsptr = workspace;
+	int inptr_offset = 0, quantptr_offset = 0, wsptr_offset = 0;
+	for (ctr = DCTSIZE; ctr > 0; ctr--) {
+		/* Due to quantization, we will usually find that many of the input
+		 * coefficients are zero, especially the AC terms.	We can exploit this
+		 * by short-circuiting the IDCT calculation for any column in which all
+		 * the AC terms are zero.	In that case each output is equal to the
+		 * DC coefficient (with scale factor as needed).
+		 * With typical images and quantization tables, half or more of the
+		 * column DCT calculations can be simplified this way.
+		 */
+
+		if (inptr[DCTSIZE*1+inptr_offset] == 0 && inptr[DCTSIZE*2+inptr_offset] == 0 &&
+			inptr[DCTSIZE*3+inptr_offset] == 0 && inptr[DCTSIZE*4+inptr_offset] == 0 &&
+			inptr[DCTSIZE*5+inptr_offset] == 0 && inptr[DCTSIZE*6+inptr_offset] == 0 &&
+			inptr[DCTSIZE*7+inptr_offset] == 0)
+		{
+			/* AC terms all zero */
+			int dcval = ((inptr[DCTSIZE*0+inptr_offset]) * quantptr[DCTSIZE*0+quantptr_offset]) << PASS1_BITS;
+
+			wsptr[DCTSIZE*0+wsptr_offset] = dcval;
+			wsptr[DCTSIZE*1+wsptr_offset] = dcval;
+			wsptr[DCTSIZE*2+wsptr_offset] = dcval;
+			wsptr[DCTSIZE*3+wsptr_offset] = dcval;
+			wsptr[DCTSIZE*4+wsptr_offset] = dcval;
+			wsptr[DCTSIZE*5+wsptr_offset] = dcval;
+			wsptr[DCTSIZE*6+wsptr_offset] = dcval;
+			wsptr[DCTSIZE*7+wsptr_offset] = dcval;
+
+			inptr_offset++;			/* advance pointers to next column */
+			quantptr_offset++;
+			wsptr_offset++;
+			continue;
+		}
+
+		/* Even part: reverse the even part of the forward DCT. */
+		/* The rotator is sqrt(2)*c(-6). */
+
+		z2 = ((inptr[DCTSIZE*2+inptr_offset]) * quantptr[DCTSIZE*2+quantptr_offset]);
+		z3 = ((inptr[DCTSIZE*6+inptr_offset]) * quantptr[DCTSIZE*6+quantptr_offset]);
+
+		z1 = ((z2 + z3) * 4433/*FIX_0_541196100*/);
+		tmp2 = z1 + (z3 * - 15137/*FIX_1_847759065*/);
+		tmp3 = z1 + (z2 * 6270/*FIX_0_765366865*/);
+
+		z2 = ((inptr[DCTSIZE*0+inptr_offset]) * quantptr[DCTSIZE*0+quantptr_offset]);
+		z3 = ((inptr[DCTSIZE*4+inptr_offset]) * quantptr[DCTSIZE*4+quantptr_offset]);
+
+		tmp0 = (z2 + z3) << CONST_BITS;
+		tmp1 = (z2 - z3) << CONST_BITS;
+
+		tmp10 = tmp0 + tmp3;
+		tmp13 = tmp0 - tmp3;
+		tmp11 = tmp1 + tmp2;
+		tmp12 = tmp1 - tmp2;
+
+		/* Odd part per figure 8; the matrix is unitary and hence its
+		 * transpose is its inverse.	i0..i3 are y7,y5,y3,y1 respectively.
+		 */
+
+		tmp0 = ((inptr[DCTSIZE*7+inptr_offset]) * quantptr[DCTSIZE*7+quantptr_offset]);
+		tmp1 = ((inptr[DCTSIZE*5+inptr_offset]) * quantptr[DCTSIZE*5+quantptr_offset]);
+		tmp2 = ((inptr[DCTSIZE*3+inptr_offset]) * quantptr[DCTSIZE*3+quantptr_offset]);
+		tmp3 = ((inptr[DCTSIZE*1+inptr_offset]) * quantptr[DCTSIZE*1+quantptr_offset]);
+
+		z1 = tmp0 + tmp3;
+		z2 = tmp1 + tmp2;
+		z3 = tmp0 + tmp2;
+		z4 = tmp1 + tmp3;
+		z5 = ((z3 + z4) * 9633/*FIX_1_175875602*/); /* sqrt(2) * c3 */
+
+		tmp0 = (tmp0 * 2446/*FIX_0_298631336*/); /* sqrt(2) * (-c1+c3+c5-c7) */
+		tmp1 = (tmp1 * 16819/*FIX_2_053119869*/); /* sqrt(2) * ( c1+c3-c5+c7) */
+		tmp2 = (tmp2 * 25172/*FIX_3_072711026*/); /* sqrt(2) * ( c1+c3+c5-c7) */
+		tmp3 = (tmp3 * 12299/*FIX_1_501321110*/); /* sqrt(2) * ( c1+c3-c5-c7) */
+		z1 = (z1 * - 7373/*FIX_0_899976223*/); /* sqrt(2) * (c7-c3) */
+		z2 = (z2 * - 20995/*FIX_2_562915447*/); /* sqrt(2) * (-c1-c3) */
+		z3 = (z3 * - 16069/*FIX_1_961570560*/); /* sqrt(2) * (-c3-c5) */
+		z4 = (z4 * - 3196/*FIX_0_390180644*/); /* sqrt(2) * (c5-c3) */
+
+		z3 += z5;
+		z4 += z5;
+
+		tmp0 += z1 + z3;
+		tmp1 += z2 + z4;
+		tmp2 += z2 + z3;
+		tmp3 += z1 + z4;
+
+		/* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+//		#define DESCALE(x,n)	RIGHT_SHIFT((x) + (ONE << ((n)-1)), n)
+		wsptr[DCTSIZE*0+wsptr_offset] = (((tmp10 + tmp3) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+		wsptr[DCTSIZE*7+wsptr_offset] = (((tmp10 - tmp3) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+		wsptr[DCTSIZE*1+wsptr_offset] = (((tmp11 + tmp2) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+		wsptr[DCTSIZE*6+wsptr_offset] = (((tmp11 - tmp2) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+		wsptr[DCTSIZE*2+wsptr_offset] = (((tmp12 + tmp1) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+		wsptr[DCTSIZE*5+wsptr_offset] = (((tmp12 - tmp1) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+		wsptr[DCTSIZE*3+wsptr_offset] = (((tmp13 + tmp0) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+		wsptr[DCTSIZE*4+wsptr_offset] = (((tmp13 - tmp0) + (1 << ((CONST_BITS-PASS1_BITS)-1))) >> (CONST_BITS-PASS1_BITS));
+
+		inptr_offset++;			/* advance pointers to next column */
+		quantptr_offset++;
+		wsptr_offset++;
+	}
+
+
+	/* Pass 2: process rows from work array, store into output array. */
+	/* Note that we must descale the results by a factor of 8 == 2**3, */
+	/* and also undo the PASS1_BITS scaling. */
+
+	int outptr_offset = 0;
+	wsptr = workspace;
+	wsptr_offset =0;
+	for (ctr = 0; ctr < DCTSIZE; ctr++) {
+		outptr = output_buf[ctr+output_buf_offset];
+		outptr_offset = output_col;
+		/* Rows of zeroes can be exploited in the same way as we did with columns.
+		 * However, the column calculation has created many nonzero AC terms, so
+		 * the simplification applies less often (typically 5% to 10% of the time).
+		 * On machines with very fast multiplication, it's possible that the
+		 * test takes more time than it's worth.	In that case this section
+		 * may be commented out.
+		 */
+
+//#ifndef NO_ZERO_ROW_TEST
+		if (wsptr[1+wsptr_offset] == 0 && wsptr[2+wsptr_offset] == 0 && wsptr[3+wsptr_offset] == 0 && wsptr[4+wsptr_offset] == 0 &&
+			wsptr[5+wsptr_offset] == 0 && wsptr[6+wsptr_offset] == 0 && wsptr[7+wsptr_offset] == 0)
+		{
+			/* AC terms all zero */
+//			#define DESCALE(x,n)	RIGHT_SHIFT((x) + (ONE << ((n)-1)), n)
+			byte dcval = range_limit[range_limit_offset + ((((wsptr[0+wsptr_offset]) + (1 << ((PASS1_BITS+3)-1))) >> PASS1_BITS+3)
+					& RANGE_MASK)];
+
+			outptr[0+outptr_offset] = dcval;
+			outptr[1+outptr_offset] = dcval;
+			outptr[2+outptr_offset] = dcval;
+			outptr[3+outptr_offset] = dcval;
+			outptr[4+outptr_offset] = dcval;
+			outptr[5+outptr_offset] = dcval;
+			outptr[6+outptr_offset] = dcval;
+			outptr[7+outptr_offset] = dcval;
+
+			wsptr_offset += DCTSIZE;		/* advance pointer to next row */
+			continue;
+		}
+//#endif
+
+		/* Even part: reverse the even part of the forward DCT. */
+		/* The rotator is sqrt(2)*c(-6). */
+
+		z2 = wsptr[2+wsptr_offset];
+		z3 = wsptr[6+wsptr_offset];
+
+		z1 = ((z2 + z3) * 4433/*FIX_0_541196100*/);
+		tmp2 = z1 + (z3 * - 15137/*FIX_1_847759065*/);
+		tmp3 = z1 + (z2 * 6270/*FIX_0_765366865*/);
+
+		tmp0 = (wsptr[0+wsptr_offset] + wsptr[4+wsptr_offset]) << CONST_BITS;
+		tmp1 = (wsptr[0+wsptr_offset] - wsptr[4+wsptr_offset]) << CONST_BITS;
+
+		tmp10 = tmp0 + tmp3;
+		tmp13 = tmp0 - tmp3;
+		tmp11 = tmp1 + tmp2;
+		tmp12 = tmp1 - tmp2;
+
+		/* Odd part per figure 8; the matrix is unitary and hence its
+		 * transpose is its inverse.	i0..i3 are y7,y5,y3,y1 respectively.
+		 */
+
+		tmp0 = wsptr[7+wsptr_offset];
+		tmp1 = wsptr[5+wsptr_offset];
+		tmp2 = wsptr[3+wsptr_offset];
+		tmp3 = wsptr[1+wsptr_offset];
+
+		z1 = tmp0 + tmp3;
+		z2 = tmp1 + tmp2;
+		z3 = tmp0 + tmp2;
+		z4 = tmp1 + tmp3;
+		z5 = ((z3 + z4) * 9633/*FIX_1_175875602*/); /* sqrt(2) * c3 */
+
+		tmp0 = (tmp0 * 2446/*FIX_0_298631336*/); /* sqrt(2) * (-c1+c3+c5-c7) */
+		tmp1 = (tmp1 * 16819/*FIX_2_053119869*/); /* sqrt(2) * ( c1+c3-c5+c7) */
+		tmp2 = (tmp2 * 25172/*FIX_3_072711026*/); /* sqrt(2) * ( c1+c3+c5-c7) */
+		tmp3 = (tmp3 * 12299/*FIX_1_501321110*/); /* sqrt(2) * ( c1+c3-c5-c7) */
+		z1 = (z1 * - 7373/*FIX_0_899976223*/); /* sqrt(2) * (c7-c3) */
+		z2 = (z2 * - 20995/*FIX_2_562915447*/); /* sqrt(2) * (-c1-c3) */
+		z3 = (z3 * - 16069/*FIX_1_961570560*/); /* sqrt(2) * (-c3-c5) */
+		z4 = (z4 * - 3196/*FIX_0_390180644*/); /* sqrt(2) * (c5-c3) */
+
+		z3 += z5;
+		z4 += z5;
+
+		tmp0 += z1 + z3;
+		tmp1 += z2 + z4;
+		tmp2 += z2 + z3;
+		tmp3 += z1 + z4;
+
+		/* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+
+//		#define DESCALE(x,n)	RIGHT_SHIFT((x) + (ONE << ((n)-1)), n)
+		outptr[0+outptr_offset] = range_limit[range_limit_offset + ((((tmp10 + tmp3) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+						CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+		outptr[7+outptr_offset] = range_limit[range_limit_offset + ((((tmp10 - tmp3) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+									CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+		outptr[1+outptr_offset] = range_limit[range_limit_offset + ((((tmp11 + tmp2) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+									CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+		outptr[6+outptr_offset] = range_limit[range_limit_offset + ((((tmp11 - tmp2) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+									CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+		outptr[2+outptr_offset] = range_limit[range_limit_offset + ((((tmp12 + tmp1) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+									CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+		outptr[5+outptr_offset] = range_limit[range_limit_offset + ((((tmp12 - tmp1) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+									CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+		outptr[3+outptr_offset] = range_limit[range_limit_offset + ((((tmp13 + tmp0) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+									CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+		outptr[4+outptr_offset] = range_limit[range_limit_offset + ((((tmp13 - tmp0) + (1 << ((CONST_BITS+PASS1_BITS+3)-1))) >>
+									CONST_BITS+PASS1_BITS+3)
+					& RANGE_MASK)];
+
+		wsptr_offset += DCTSIZE;		/* advance pointer to next row */
+	}
+}
+
+static void upsample (jpeg_decompress_struct cinfo,
+	byte[][][] input_buf, int[] input_buf_offset, int[] in_row_group_ctr,
+	int in_row_groups_avail,
+	byte[][] output_buf, int[] out_row_ctr,
+	int out_rows_avail)
+{
+	sep_upsample(cinfo, input_buf, input_buf_offset, in_row_group_ctr, in_row_groups_avail, output_buf, out_row_ctr, out_rows_avail);
+}
+
+static bool smoothing_ok (jpeg_decompress_struct cinfo) {
+	jpeg_d_coef_controller coef = cinfo.coef;
+	bool smoothing_useful = false;
+	int ci, coefi;
+	jpeg_component_info compptr;
+	JQUANT_TBL qtable;
+	int[] coef_bits;
+	int[] coef_bits_latch;
+
+	if (! cinfo.progressive_mode || cinfo.coef_bits == null)
+		return false;
+
+	/* Allocate latch area if not already done */
+	if (coef.coef_bits_latch == null)
+		coef.coef_bits_latch = new int[cinfo.num_components * SAVED_COEFS];
+	coef_bits_latch = coef.coef_bits_latch;
+	int coef_bits_latch_offset = 0;
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		/* All components' quantization values must already be latched. */
+		if ((qtable = compptr.quant_table) == null)
+			return false;
+		/* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */
+		if (qtable.quantval[0] == 0 ||
+			qtable.quantval[Q01_POS] == 0 ||
+			qtable.quantval[Q10_POS] == 0 ||
+			qtable.quantval[Q20_POS] == 0 ||
+			qtable.quantval[Q11_POS] == 0 ||
+			qtable.quantval[Q02_POS] == 0)
+				return false;
+		/* DC values must be at least partly known for all components. */
+		coef_bits = cinfo.coef_bits[ci];
+		if (coef_bits[0] < 0)
+			return false;
+		/* Block smoothing is helpful if some AC coefficients remain inaccurate. */
+		for (coefi = 1; coefi <= 5; coefi++) {
+			coef_bits_latch[coefi+coef_bits_latch_offset] = coef_bits[coefi];
+			if (coef_bits[coefi] != 0)
+				smoothing_useful = true;
+		}
+		coef_bits_latch_offset += SAVED_COEFS;
+	}
+
+	return smoothing_useful;
+}
+
+static void master_selection (jpeg_decompress_struct cinfo) {
+	jpeg_decomp_master master = cinfo.master;
+	bool use_c_buffer;
+	long samplesperrow;
+	int jd_samplesperrow;
+
+	/* Initialize dimensions and other stuff */
+	jpeg_calc_output_dimensions(cinfo);
+	prepare_range_limit_table(cinfo);
+
+	/* Width of an output scanline must be representable as JDIMENSION. */
+	samplesperrow = cast(long) cinfo.output_width * cast(long) cinfo.out_color_components;
+	jd_samplesperrow = cast(int) samplesperrow;
+	if ( jd_samplesperrow != samplesperrow)
+		error();
+//		ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
+
+	/* Initialize my private state */
+	master.pass_number = 0;
+	master.using_merged_upsample = use_merged_upsample(cinfo);
+
+	/* Color quantizer selection */
+	master.quantizer_1pass = null;
+	master.quantizer_2pass = null;
+	/* No mode changes if not using buffered-image mode. */
+	if (! cinfo.quantize_colors || ! cinfo.buffered_image) {
+		cinfo.enable_1pass_quant = false;
+		cinfo.enable_external_quant = false;
+		cinfo.enable_2pass_quant = false;
+	}
+	if (cinfo.quantize_colors) {
+		error(SWT.ERROR_NOT_IMPLEMENTED);
+//		if (cinfo.raw_data_out)
+//			ERREXIT(cinfo, JERR_NOTIMPL);
+//		/* 2-pass quantizer only works in 3-component color space. */
+//		if (cinfo.out_color_components != 3) {
+//			cinfo.enable_1pass_quant = true;
+//			cinfo.enable_external_quant = false;
+//			cinfo.enable_2pass_quant = false;
+//			cinfo.colormap = null;
+//		} else if (cinfo.colormap != null) {
+//			cinfo.enable_external_quant = true;
+//		} else if (cinfo.two_pass_quantize) {
+//			cinfo.enable_2pass_quant = true;
+//		} else {
+//			cinfo.enable_1pass_quant = true;
+//		}
+//
+//		if (cinfo.enable_1pass_quant) {
+//#ifdef QUANT_1PASS_SUPPORTED
+//			jinit_1pass_quantizer(cinfo);
+//			master.quantizer_1pass = cinfo.cquantize;
+//#else
+//			ERREXIT(cinfo, JERR_NOT_COMPILED);
+//#endif
+//		}
+//
+//		/* We use the 2-pass code to map to external colormaps. */
+//		if (cinfo.enable_2pass_quant || cinfo.enable_external_quant) {
+//#ifdef QUANT_2PASS_SUPPORTED
+//			jinit_2pass_quantizer(cinfo);
+//			master.quantizer_2pass = cinfo.cquantize;
+//#else
+//			ERREXIT(cinfo, JERR_NOT_COMPILED);
+//#endif
+//		}
+//		/* If both quantizers are initialized, the 2-pass one is left active;
+//		 * this is necessary for starting with quantization to an external map.
+//		 */
+	}
+
+	/* Post-processing: in particular, color conversion first */
+	if (! cinfo.raw_data_out) {
+		if (master.using_merged_upsample) {
+//#ifdef UPSAMPLE_MERGING_SUPPORTED
+//			jinit_merged_upsampler(cinfo); /* does color conversion too */
+//#else
+			error();
+//			ERREXIT(cinfo, JERR_NOT_COMPILED);
+//#endif
+		} else {
+			jinit_color_deconverter(cinfo);
+			jinit_upsampler(cinfo);
+		}
+		jinit_d_post_controller(cinfo, cinfo.enable_2pass_quant);
+	}
+	/* Inverse DCT */
+	jinit_inverse_dct(cinfo);
+	/* Entropy decoding: either Huffman or arithmetic coding. */
+	if (cinfo.arith_code) {
+		error();
+//		ERREXIT(cinfo, JERR_ARITH_NOTIMPL);
+	} else {
+		if (cinfo.progressive_mode) {
+//#ifdef D_PROGRESSIVE_SUPPORTED
+			jinit_phuff_decoder(cinfo);
+//#else
+//			ERREXIT(cinfo, JERR_NOT_COMPILED);
+//#endif
+		} else
+			jinit_huff_decoder(cinfo);
+	}
+
+	/* Initialize principal buffer controllers. */
+	use_c_buffer = cinfo.inputctl.has_multiple_scans || cinfo.buffered_image;
+	jinit_d_coef_controller(cinfo, use_c_buffer);
+
+	if (! cinfo.raw_data_out)
+		jinit_d_main_controller(cinfo, false /* never need full buffer here */);
+
+	/* Initialize input side of decompressor to consume first scan. */
+	start_input_pass (cinfo);
+
+//#ifdef D_MULTISCAN_FILES_SUPPORTED
+	/* If jpeg_start_decompress will read the whole file, initialize
+	 * progress monitoring appropriately.	The input step is counted
+	 * as one pass.
+	 */
+//	if (cinfo.progress != null && ! cinfo.buffered_image &&
+//			cinfo.inputctl.has_multiple_scans) {
+//		int nscans;
+//		/* Estimate number of scans to set pass_limit. */
+//		if (cinfo.progressive_mode) {
+//			/* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */
+//			nscans = 2 + 3 * cinfo.num_components;
+//		} else {
+//			/* For a nonprogressive multiscan file, estimate 1 scan per component. */
+//			nscans = cinfo.num_components;
+//		}
+//		cinfo.progress.pass_counter = 0L;
+//		cinfo.progress.pass_limit = cast(long) cinfo.total_iMCU_rows * nscans;
+//		cinfo.progress.completed_passes = 0;
+//		cinfo.progress.total_passes = (cinfo.enable_2pass_quant ? 3 : 2);
+//		/* Count the input pass as done */
+//		master.pass_number++;
+//	}
+//#endif /* D_MULTISCAN_FILES_SUPPORTED */
+}
+
+static void jinit_master_decompress (jpeg_decompress_struct cinfo) {
+	jpeg_decomp_master master = new jpeg_decomp_master();
+	cinfo.master = master;
+//	master.prepare_for_output_pass = prepare_for_output_pass;
+//	master.finish_output_pass = finish_output_pass;
+
+	master.is_dummy_pass = false;
+
+	master_selection(cinfo);
+}
+
+static void
+jcopy_sample_rows (byte[][] input_array, int source_row,
+		   byte[][] output_array, int dest_row,
+		   int num_rows, int num_cols)
+/* Copy some rows of samples from one place to another.
+ * num_rows rows are copied from input_array[source_row++]
+ * to output_array[dest_row++]; these areas may overlap for duplication.
+ * The source and destination arrays must be at least as wide as num_cols.
+ */
+{
+  byte[] inptr, outptr;
+  int count = num_cols;
+  int row;
+
+  int input_array_offset = source_row;
+  int output_array_offset = dest_row;
+
+  for (row = num_rows; row > 0; row--) {
+    inptr = input_array[input_array_offset++];
+    outptr = output_array[output_array_offset++];
+    outptr[ 0 .. count ] = inptr[ 0 .. count ];
+  }
+}
+
+static bool jpeg_start_decompress (jpeg_decompress_struct cinfo) {
+	if (cinfo.global_state == DSTATE_READY) {
+		/* First call: initialize master control, select active modules */
+		jinit_master_decompress(cinfo);
+		if (cinfo.buffered_image) {
+			/* No more work here; expecting jpeg_start_output next */
+			cinfo.global_state = DSTATE_BUFIMAGE;
+			return true;
+		}
+		cinfo.global_state = DSTATE_PRELOAD;
+	}
+	if (cinfo.global_state == DSTATE_PRELOAD) {
+		/* If file has multiple scans, absorb them all into the coef buffer */
+		if (cinfo.inputctl.has_multiple_scans) {
+//#ifdef D_MULTISCAN_FILES_SUPPORTED
+			for (;;) {
+				int retcode;
+				/* Call progress monitor hook if present */
+//				if (cinfo.progress != null)
+//					(*cinfo.progress.progress_monitor) ((j_common_ptr) cinfo);
+				/* Absorb some more input */
+				retcode = consume_input (cinfo);
+				if (retcode == JPEG_SUSPENDED)
+					return false;
+				if (retcode == JPEG_REACHED_EOI)
+					break;
+				/* Advance progress counter if appropriate */
+//				if (cinfo.progress != null && (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) {
+//					if (++cinfo.progress.pass_counter >= cinfo.progress.pass_limit) {
+//						/* jdmaster underestimated number of scans; ratchet up one scan */
+//						cinfo.progress.pass_limit += cast(long) cinfo.total_iMCU_rows;
+//					}
+//				}
+			}
+//#else
+//			ERREXIT(cinfo, JERR_NOT_COMPILED);
+//#endif /* D_MULTISCAN_FILES_SUPPORTED */
+		}
+		cinfo.output_scan_number = cinfo.input_scan_number;
+	} else if (cinfo.global_state != DSTATE_PRESCAN)
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+	/* Perform any dummy output passes, and set up for the final pass */
+	return output_pass_setup(cinfo);
+}
+
+static void prepare_for_output_pass (jpeg_decompress_struct cinfo) {
+	jpeg_decomp_master master = cinfo.master;
+
+	if (master.is_dummy_pass) {
+//#ifdef QUANT_2PASS_SUPPORTED
+//		/* Final pass of 2-pass quantization */
+//		master.pub.is_dummy_pass = FALSE;
+//		(*cinfo.cquantize.start_pass) (cinfo, FALSE);
+//		(*cinfo.post.start_pass) (cinfo, JBUF_CRANK_DEST);
+//		(*cinfo.main.start_pass) (cinfo, JBUF_CRANK_DEST);
+//#else
+		error(SWT.ERROR_NOT_IMPLEMENTED);
+//		ERREXIT(cinfo, JERR_NOT_COMPILED);
+//#endif /* QUANT_2PASS_SUPPORTED */
+	} else {
+		if (cinfo.quantize_colors && cinfo.colormap == null) {
+			/* Select new quantization method */
+			if (cinfo.two_pass_quantize && cinfo.enable_2pass_quant) {
+				cinfo.cquantize = master.quantizer_2pass;
+				master.is_dummy_pass = true;
+			} else if (cinfo.enable_1pass_quant) {
+				cinfo.cquantize = master.quantizer_1pass;
+			} else {
+				error();
+//	ERREXIT(cinfo, JERR_MODE_CHANGE);
+			}
+		}
+		cinfo.idct.start_pass (cinfo);
+		start_output_pass (cinfo);
+		if (! cinfo.raw_data_out) {
+			if (! master.using_merged_upsample)
+				cinfo.cconvert.start_pass (cinfo);
+			cinfo.upsample.start_pass (cinfo);
+			if (cinfo.quantize_colors)
+				cinfo.cquantize.start_pass (cinfo, master.is_dummy_pass);
+			cinfo.post.start_pass (cinfo, (master.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU));
+			cinfo.main.start_pass (cinfo, JBUF_PASS_THRU);
+		}
+	}
+
+//	/* Set up progress monitor's pass info if present */
+//	if (cinfo.progress != NULL) {
+//		cinfo.progress.completed_passes = master.pass_number;
+//		cinfo.progress.total_passes = master.pass_number +
+//						(master.pub.is_dummy_pass ? 2 : 1);
+//		/* In buffered-image mode, we assume one more output pass if EOI not
+//		 * yet reached, but no more passes if EOI has been reached.
+//		 */
+//		if (cinfo.buffered_image && ! cinfo.inputctl.eoi_reached) {
+//			cinfo.progress.total_passes += (cinfo.enable_2pass_quant ? 2 : 1);
+//		}
+//	}
+}
+
+
+static bool jpeg_resync_to_restart (jpeg_decompress_struct cinfo, int desired) {
+	int marker = cinfo.unread_marker;
+	int action = 1;
+
+	/* Always put up a warning. */
+//	WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired);
+
+	/* Outer loop handles repeated decision after scanning forward. */
+	for (;;) {
+		if (marker < M_SOF0)
+			action = 2;		/* invalid marker */
+		else if (marker < M_RST0 || marker > M_RST7)
+			action = 3;		/* valid non-restart marker */
+		else {
+			if (marker == (M_RST0 + ((desired+1) & 7)) || marker == ( M_RST0 + ((desired+2) & 7)))
+				action = 3;		/* one of the next two expected restarts */
+			else if (marker == (M_RST0 + ((desired-1) & 7)) || marker == ( M_RST0 + ((desired-2) & 7)))
+				action = 2;		/* a prior restart, so advance */
+			else
+				action = 1;		/* desired restart or too far away */
+		}
+//		TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action);
+		switch (action) {
+			case 1:
+				/* Discard marker and let entropy decoder resume processing. */
+				cinfo.unread_marker = 0;
+				return true;
+			case 2:
+				/* Scan to the next marker, and repeat the decision loop. */
+				if (! next_marker(cinfo))
+					return false;
+				marker = cinfo.unread_marker;
+				break;
+			case 3:
+				/* Return without advancing past this marker. */
+				/* Entropy decoder will be forced to process an empty segment. */
+				return true;
+		}
+	} /* end loop */
+}
+
+static bool read_restart_marker (jpeg_decompress_struct cinfo) {
+	/* Obtain a marker unless we already did. */
+	/* Note that next_marker will complain if it skips any data. */
+	if (cinfo.unread_marker == 0) {
+		if (! next_marker(cinfo))
+			return false;
+	}
+
+	if (cinfo.unread_marker == (M_RST0 + cinfo.marker.next_restart_num)) {
+		/* Normal case --- swallow the marker and let entropy decoder continue */
+//		TRACEMS1(cinfo, 3, JTRC_RST, cinfo.marker.next_restart_num);
+		cinfo.unread_marker = 0;
+	} else {
+		/* Uh-oh, the restart markers have been messed up. */
+		/* Let the data source manager determine how to resync. */
+		if (! jpeg_resync_to_restart (cinfo, cinfo.marker.next_restart_num))
+			return false;
+	}
+
+	/* Update next-restart state */
+	cinfo.marker.next_restart_num = (cinfo.marker.next_restart_num + 1) & 7;
+
+	return true;
+}
+
+static bool jpeg_fill_bit_buffer (bitread_working_state state, int get_buffer, int bits_left, int nbits)
+/* Load up the bit buffer to a depth of at least nbits */
+{
+	/* Copy heavily used state fields into locals (hopefully registers) */
+	byte[] buffer = state.buffer;
+	int bytes_in_buffer = state.bytes_in_buffer;
+	int bytes_offset = state.bytes_offset;
+	jpeg_decompress_struct cinfo = state.cinfo;
+
+	/* Attempt to load at least MIN_GET_BITS bits into get_buffer. */
+	/* (It is assumed that no request will be for more than that many bits.) */
+	/* We fail to do so only if we hit a marker or are forced to suspend. */
+
+	if (cinfo.unread_marker == 0) {	/* cannot advance past a marker */
+		while (bits_left < MIN_GET_BITS) {
+			int c;
+
+			/* Attempt to read a byte */
+			if (bytes_offset == bytes_in_buffer) {
+				if (! fill_input_buffer (cinfo))
+					return false;
+				buffer = cinfo.buffer;
+				bytes_in_buffer = cinfo.bytes_in_buffer;
+				bytes_offset = cinfo.bytes_offset;
+			}
+			c = buffer[bytes_offset++] & 0xFF;
+
+			/* If it's 0xFF, check and discard stuffed zero byte */
+			if (c == 0xFF) {
+				/* Loop here to discard any padding FF's on terminating marker,
+				 * so that we can save a valid unread_marker value.	NOTE: we will
+				 * accept multiple FF's followed by a 0 as meaning a single FF data
+				 * byte.	This data pattern is not valid according to the standard.
+				 */
+				do {
+					if (bytes_offset == bytes_in_buffer) {
+						if (! fill_input_buffer (cinfo))
+							return false;
+						buffer = cinfo.buffer;
+						bytes_in_buffer = cinfo.bytes_in_buffer;
+						bytes_offset = cinfo.bytes_offset;
+					}
+					c = buffer[bytes_offset++] & 0xFF;
+				} while (c == 0xFF);
+
+				if (c == 0) {
+					/* Found FF/00, which represents an FF data byte */
+					c = 0xFF;
+				} else {
+					/* Oops, it's actually a marker indicating end of compressed data.
+					 * Save the marker code for later use.
+					 * Fine point: it might appear that we should save the marker into
+					 * bitread working state, not straight into permanent state.	But
+					 * once we have hit a marker, we cannot need to suspend within the
+					 * current MCU, because we will read no more bytes from the data
+					 * source.	So it is OK to update permanent state right away.
+					 */
+					cinfo.unread_marker = c;
+					/* See if we need to insert some fake zero bits. */
+//					goto no_more_bytes;
+					if (nbits > bits_left) {
+						/* Uh-oh.	Report corrupted data to user and stuff zeroes into
+						 * the data stream, so that we can produce some kind of image.
+						 * We use a nonvolatile flag to ensure that only one warning message
+						 * appears per data segment.
+						 */
+						if (! cinfo.entropy.insufficient_data) {
+//							WARNMS(cinfo, JWRN_HIT_MARKER);
+							cinfo.entropy.insufficient_data = true;
+						}
+					/* Fill the buffer with zero bits */
+						get_buffer <<= MIN_GET_BITS - bits_left;
+						bits_left = MIN_GET_BITS;
+					}
+
+					/* Unload the local registers */
+					state.buffer = buffer;
+					state.bytes_in_buffer = bytes_in_buffer;
+					state.bytes_offset = bytes_offset;
+					state.get_buffer = get_buffer;
+					state.bits_left = bits_left;
+
+					return true;
+
+				}
+			}
+
+			/* OK, load c into get_buffer */
+			get_buffer = (get_buffer << 8) | c;
+			bits_left += 8;
+		} /* end while */
+	} else {
+//		no_more_bytes:
+		/* We get here if we've read the marker that terminates the compressed
+		 * data segment.	There should be enough bits in the buffer register
+		 * to satisfy the request; if so, no problem.
+		 */
+		if (nbits > bits_left) {
+			/* Uh-oh.	Report corrupted data to user and stuff zeroes into
+			 * the data stream, so that we can produce some kind of image.
+			 * We use a nonvolatile flag to ensure that only one warning message
+			 * appears per data segment.
+			 */
+			if (! cinfo.entropy.insufficient_data) {
+//				WARNMS(cinfo, JWRN_HIT_MARKER);
+				cinfo.entropy.insufficient_data = true;
+			}
+			/* Fill the buffer with zero bits */
+			get_buffer <<= MIN_GET_BITS - bits_left;
+			bits_left = MIN_GET_BITS;
+		}
+	}
+
+	/* Unload the local registers */
+	state.buffer = buffer;
+	state.bytes_in_buffer = bytes_in_buffer;
+	state.bytes_offset = bytes_offset;
+	state.get_buffer = get_buffer;
+	state.bits_left = bits_left;
+
+	return true;
+}
+
+static int jpeg_huff_decode (bitread_working_state state, int get_buffer, int bits_left, d_derived_tbl htbl, int min_bits) {
+	int l = min_bits;
+	int code;
+
+	/* HUFF_DECODE has determined that the code is at least min_bits */
+	/* bits long, so fetch that many bits in one swoop. */
+
+//	CHECK_BIT_BUFFER(*state, l, return -1);
+	{
+	if (bits_left < (l)) {
+		if (! jpeg_fill_bit_buffer(state,get_buffer,bits_left,l)) {
+			return -1;
+		}
+		get_buffer = state.get_buffer; bits_left = state.bits_left;
+	}
+	}
+//	code = GET_BITS(l);
+	code = (( (get_buffer >> (bits_left -= (l)))) & ((1<<(l))-1));
+
+	/* Collect the rest of the Huffman code one bit at a time. */
+	/* This is per Figure F.16 in the JPEG spec. */
+
+	while (code > htbl.maxcode[l]) {
+		code <<= 1;
+//		CHECK_BIT_BUFFER(*state, 1, return -1);
+		{
+		if (bits_left < (1)) {
+			if (! jpeg_fill_bit_buffer(state,get_buffer,bits_left,1)) {
+				return -1;
+			}
+			get_buffer = state.get_buffer; bits_left = state.bits_left;
+		}
+		}
+//		code |= GET_BITS(1);
+		code |= (( (get_buffer >> (bits_left -= (1)))) & ((1<<(1))-1));
+		l++;
+	}
+
+	/* Unload the local registers */
+	state.get_buffer = get_buffer;
+	state.bits_left = bits_left;
+
+	/* With garbage input we may reach the sentinel value l = 17. */
+
+	if (l > 16) {
+//		WARNMS(state.cinfo, JWRN_HUFF_BAD_CODE);
+		return 0;			/* fake a zero as the safest result */
+	}
+
+	return htbl.pub.huffval[ (code + htbl.valoffset[l]) ] & 0xFF;
+}
+
+static int decompress_onepass (jpeg_decompress_struct cinfo, byte[][][] output_buf, int[] output_buf_offset) {
+	jpeg_d_coef_controller coef = cinfo.coef;
+	int MCU_col_num;	/* index of current MCU within row */
+	int last_MCU_col = cinfo.MCUs_per_row - 1;
+	int last_iMCU_row = cinfo.total_iMCU_rows - 1;
+	int blkn, ci, xindex, yindex, yoffset, useful_width;
+	byte[][] output_ptr;
+	int start_col, output_col;
+	jpeg_component_info compptr;
+//	inverse_DCT_method_ptr inverse_DCT;
+
+	/* Loop to process as much as one whole iMCU row */
+	for (yoffset = coef.MCU_vert_offset; yoffset < coef.MCU_rows_per_iMCU_row; yoffset++) {
+		for (MCU_col_num = coef.MCU_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) {
+			/* Try to fetch an MCU.	Entropy decoder expects buffer to be zeroed. */
+			for (int i = 0; i < cinfo.blocks_in_MCU; i++) {
+				short[] blk = coef.MCU_buffer[i];
+				for (int j = 0; j < blk.length; j++) {
+					blk[j] = 0;
+				}
+			}
+			if (! cinfo.entropy.decode_mcu (cinfo, coef.MCU_buffer)) {
+				/* Suspension forced; update state counters and exit */
+				coef.MCU_vert_offset = yoffset;
+				coef.MCU_ctr = MCU_col_num;
+				return JPEG_SUSPENDED;
+			}
+			/* Determine where data should go in output_buf and do the IDCT thing.
+			 * We skip dummy blocks at the right and bottom edges (but blkn gets
+			 * incremented past them!).	Note the inner loop relies on having
+			 * allocated the MCU_buffer[] blocks sequentially.
+			 */
+			blkn = 0;			/* index of current DCT block within MCU */
+			for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+				compptr = cinfo.cur_comp_info[ci];
+				/* Don't bother to IDCT an uninteresting component. */
+				if (! compptr.component_needed) {
+					blkn += compptr.MCU_blocks;
+					continue;
+				}
+//				inverse_DCT = cinfo.idct.inverse_DCT[compptr.component_index];
+				useful_width = (MCU_col_num < last_MCU_col) ? compptr.MCU_width	: compptr.last_col_width;
+				output_ptr = output_buf[compptr.component_index];
+				int output_ptr_offset = output_buf_offset[compptr.component_index] + yoffset * compptr.DCT_scaled_size;
+				start_col = MCU_col_num * compptr.MCU_sample_width;
+				for (yindex = 0; yindex < compptr.MCU_height; yindex++) {
+					if (cinfo.input_iMCU_row < last_iMCU_row ||	yoffset+yindex < compptr.last_row_height) {
+						output_col = start_col;
+						for (xindex = 0; xindex < useful_width; xindex++) {
+							jpeg_idct_islow(cinfo, compptr, coef.MCU_buffer[blkn+xindex], output_ptr, output_ptr_offset, output_col);
+							output_col += compptr.DCT_scaled_size;
+						}
+					}
+					blkn += compptr.MCU_width;
+					output_ptr_offset += compptr.DCT_scaled_size;
+				}
+			}
+		}
+		/* Completed an MCU row, but perhaps not an iMCU row */
+		coef.MCU_ctr = 0;
+	}
+	/* Completed the iMCU row, advance counters for next one */
+	cinfo.output_iMCU_row++;
+	if (++(cinfo.input_iMCU_row) < cinfo.total_iMCU_rows) {
+		coef.start_iMCU_row(cinfo);
+		return JPEG_ROW_COMPLETED;
+	}
+	/* Completed the scan */
+	finish_input_pass (cinfo);
+	return JPEG_SCAN_COMPLETED;
+}
+
+static int decompress_smooth_data (jpeg_decompress_struct cinfo, byte[][][] output_buf, int[] output_buf_offset) {
+	jpeg_d_coef_controller coef = cinfo.coef;
+	int last_iMCU_row = cinfo.total_iMCU_rows - 1;
+	int block_num, last_block_column;
+	int ci, block_row, block_rows, access_rows;
+	short[][][] buffer;
+	short[][] buffer_ptr, prev_block_row, next_block_row;
+	byte[][] output_ptr;
+	int output_col;
+	jpeg_component_info compptr;
+//	inverse_DCT_method_ptr inverse_DCT;
+	bool first_row, last_row;
+	short[] workspace = coef.workspace;
+	if (workspace == null) workspace = coef.workspace = new short[DCTSIZE2];
+	int[] coef_bits;
+	JQUANT_TBL quanttbl;
+	int Q00,Q01,Q02,Q10,Q11,Q20, num;
+	int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9;
+	int Al, pred;
+
+	/* Force some input to be done if we are getting ahead of the input. */
+	while (cinfo.input_scan_number <= cinfo.output_scan_number && ! cinfo.inputctl.eoi_reached) {
+		if (cinfo.input_scan_number == cinfo.output_scan_number) {
+			/* If input is working on current scan, we ordinarily want it to
+			 * have completed the current row.	But if input scan is DC,
+			 * we want it to keep one row ahead so that next block row's DC
+			 * values are up to date.
+			 */
+			int delta = (cinfo.Ss == 0) ? 1 : 0;
+			if (cinfo.input_iMCU_row > cinfo.output_iMCU_row+delta)
+				break;
+		}
+		if (consume_input(cinfo) == JPEG_SUSPENDED)
+			return JPEG_SUSPENDED;
+	}
+
+	/* OK, output from the virtual arrays. */
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		/* Don't bother to IDCT an uninteresting component. */
+		if (! compptr.component_needed)
+			continue;
+		/* Count non-dummy DCT block rows in this iMCU row. */
+		if (cinfo.output_iMCU_row < last_iMCU_row) {
+			block_rows = compptr.v_samp_factor;
+			access_rows = block_rows * 2; /* this and next iMCU row */
+			last_row = false;
+		} else {
+			/* NB: can't use last_row_height here; it is input-side-dependent! */
+			block_rows = (compptr.height_in_blocks % compptr.v_samp_factor);
+			if (block_rows == 0) block_rows = compptr.v_samp_factor;
+			access_rows = block_rows; /* this iMCU row only */
+			last_row = true;
+		}
+		/* Align the virtual buffer for this component. */
+		int buffer_offset;
+		if (cinfo.output_iMCU_row > 0) {
+			access_rows += compptr.v_samp_factor; /* prior iMCU row too */
+			buffer = coef.whole_image[ci];
+			buffer_offset = (cinfo.output_iMCU_row - 1) * compptr.v_samp_factor;
+			buffer_offset += compptr.v_samp_factor;	/* point to current iMCU row */
+			first_row = false;
+		} else {
+			buffer = coef.whole_image[ci];
+			buffer_offset = 0;
+			first_row = true;
+		}
+		/* Fetch component-dependent info */
+		coef_bits = coef.coef_bits_latch;
+		int coef_offset = (ci * SAVED_COEFS);
+		quanttbl = compptr.quant_table;
+		Q00 = quanttbl.quantval[0];
+		Q01 = quanttbl.quantval[Q01_POS];
+		Q10 = quanttbl.quantval[Q10_POS];
+		Q20 = quanttbl.quantval[Q20_POS];
+		Q11 = quanttbl.quantval[Q11_POS];
+		Q02 = quanttbl.quantval[Q02_POS];
+//		inverse_DCT = cinfo.idct.inverse_DCT[ci];
+		output_ptr = output_buf[ci];
+		int output_ptr_offset = output_buf_offset[ci];
+		/* Loop over all DCT blocks to be processed. */
+		for (block_row = 0; block_row < block_rows; block_row++) {
+			buffer_ptr = buffer[block_row+buffer_offset];
+			int buffer_ptr_offset = 0, prev_block_row_offset = 0, next_block_row_offset = 0;
+			if (first_row && block_row == 0) {
+				prev_block_row = buffer_ptr;
+				prev_block_row_offset = buffer_ptr_offset;
+			} else {
+				prev_block_row = buffer[block_row-1+buffer_offset];
+				prev_block_row_offset = 0;
+			}
+			if (last_row && block_row == block_rows-1) {
+				next_block_row = buffer_ptr;
+				next_block_row_offset = buffer_ptr_offset;
+			} else {
+				next_block_row = buffer[block_row+1+buffer_offset];
+				next_block_row_offset = 0;
+			}
+			/* We fetch the surrounding DC values using a sliding-register approach.
+			 * Initialize all nine here so as to do the right thing on narrow pics.
+			 */
+			DC1 = DC2 = DC3 = prev_block_row[0+prev_block_row_offset][0];
+			DC4 = DC5 = DC6 = buffer_ptr[0+buffer_ptr_offset][0];
+			DC7 = DC8 = DC9 = next_block_row[0+next_block_row_offset][0];
+			output_col = 0;
+			last_block_column = compptr.width_in_blocks - 1;
+			for (block_num = 0; block_num <= last_block_column; block_num++) {
+				/* Fetch current DCT block into workspace so we can modify it. */
+//				jcopy_block_row(buffer_ptr, workspace, 1);
+                workspace[] = buffer_ptr[buffer_ptr_offset][ 0 .. workspace.length ];
+				/* Update DC values */
+				if (block_num < last_block_column) {
+					DC3 = prev_block_row[1+prev_block_row_offset][0];
+					DC6 = buffer_ptr[1+buffer_ptr_offset][0];
+					DC9 = next_block_row[1+next_block_row_offset][0];
+				}
+				/* Compute coefficient estimates per K.8.
+				 * An estimate is applied only if coefficient is still zero,
+				 * and is not known to be fully accurate.
+				 */
+				/* AC01 */
+				if ((Al=coef_bits[1+coef_offset]) != 0 && workspace[1] == 0) {
+					num = 36 * Q00 * (DC4 - DC6);
+					if (num >= 0) {
+						pred = (((Q01<<7) + num) / (Q01<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+					} else {
+						pred = (((Q01<<7) - num) / (Q01<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+						pred = -pred;
+					}
+					workspace[1] = cast(short) pred;
+				}
+				/* AC10 */
+				if ((Al=coef_bits[2+coef_offset]) != 0 && workspace[8] == 0) {
+					num = 36 * Q00 * (DC2 - DC8);
+					if (num >= 0) {
+						pred = (((Q10<<7) + num) / (Q10<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+					} else {
+						pred = (((Q10<<7) - num) / (Q10<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+						pred = -pred;
+					}
+					workspace[8] = cast(short) pred;
+				}
+				/* AC20 */
+				if ((Al=coef_bits[3+coef_offset]) != 0 && workspace[16] == 0) {
+					num = 9 * Q00 * (DC2 + DC8 - 2*DC5);
+					if (num >= 0) {
+						pred = (((Q20<<7) + num) / (Q20<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+					} else {
+						pred = (((Q20<<7) - num) / (Q20<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+						pred = -pred;
+					}
+					workspace[16] = cast(short) pred;
+				}
+				/* AC11 */
+				if ((Al=coef_bits[4+coef_offset]) != 0 && workspace[9] == 0) {
+					num = 5 * Q00 * (DC1 - DC3 - DC7 + DC9);
+					if (num >= 0) {
+						pred = (((Q11<<7) + num) / (Q11<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+					} else {
+						pred = (((Q11<<7) - num) / (Q11<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+						pred = -pred;
+					}
+					workspace[9] = cast(short) pred;
+				}
+				/* AC02 */
+				if ((Al=coef_bits[5+coef_offset]) != 0 && workspace[2] == 0) {
+					num = 9 * Q00 * (DC4 + DC6 - 2*DC5);
+					if (num >= 0) {
+						pred = (((Q02<<7) + num) / (Q02<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+					} else {
+						pred = (((Q02<<7) - num) / (Q02<<8));
+						if (Al > 0 && pred >= (1<<Al))
+							pred = (1<<Al)-1;
+						pred = -pred;
+					}
+					workspace[2] = cast(short) pred;
+				}
+				/* OK, do the IDCT */
+				jpeg_idct_islow(cinfo, compptr, workspace, output_ptr, output_ptr_offset, output_col);
+				/* Advance for next column */
+				DC1 = DC2; DC2 = DC3;
+				DC4 = DC5; DC5 = DC6;
+				DC7 = DC8; DC8 = DC9;
+				buffer_ptr_offset++; prev_block_row_offset++; next_block_row_offset++;
+				output_col += compptr.DCT_scaled_size;
+			}
+			output_ptr_offset += compptr.DCT_scaled_size;
+		}
+	}
+
+	if (++(cinfo.output_iMCU_row) < cinfo.total_iMCU_rows)
+		return JPEG_ROW_COMPLETED;
+	return JPEG_SCAN_COMPLETED;
+}
+
+static int decompress_data (jpeg_decompress_struct cinfo, byte[][][] output_buf, int[] output_buf_offset) {
+	jpeg_d_coef_controller coef = cinfo.coef;
+	int last_iMCU_row = cinfo.total_iMCU_rows - 1;
+	int block_num;
+	int ci, block_row, block_rows;
+	short[][][] buffer;
+	short[][] buffer_ptr;
+	byte[][] output_ptr;
+	int output_col;
+	jpeg_component_info compptr;
+//	inverse_DCT_method_ptr inverse_DCT;
+
+	/* Force some input to be done if we are getting ahead of the input. */
+	while (cinfo.input_scan_number < cinfo.output_scan_number ||
+	 (cinfo.input_scan_number == cinfo.output_scan_number &&
+		cinfo.input_iMCU_row <= cinfo.output_iMCU_row))
+	{
+		if (consume_input(cinfo) == JPEG_SUSPENDED)
+			return JPEG_SUSPENDED;
+	}
+
+	/* OK, output from the virtual arrays. */
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		/* Don't bother to IDCT an uninteresting component. */
+		if (! compptr.component_needed)
+			continue;
+		/* Align the virtual buffer for this component. */
+		buffer = coef.whole_image[ci];
+		int buffer_offset = cinfo.output_iMCU_row * compptr.v_samp_factor;
+		/* Count non-dummy DCT block rows in this iMCU row. */
+		if (cinfo.output_iMCU_row < last_iMCU_row)
+			block_rows = compptr.v_samp_factor;
+		else {
+			/* NB: can't use last_row_height here; it is input-side-dependent! */
+			block_rows = (compptr.height_in_blocks % compptr.v_samp_factor);
+			if (block_rows == 0) block_rows = compptr.v_samp_factor;
+		}
+//		inverse_DCT = cinfo.idct.inverse_DCT[ci];
+		output_ptr = output_buf[ci];
+		int output_ptr_offset = output_buf_offset[ci];
+		/* Loop over all DCT blocks to be processed. */
+		for (block_row = 0; block_row < block_rows; block_row++) {
+			buffer_ptr = buffer[block_row+buffer_offset];
+			int buffer_ptr_offset = 0;
+			output_col = 0;
+			for (block_num = 0; block_num < compptr.width_in_blocks; block_num++) {
+				jpeg_idct_islow(cinfo, compptr, buffer_ptr[buffer_ptr_offset], output_ptr, output_ptr_offset, output_col);
+
+				buffer_ptr_offset++;
+				output_col += compptr.DCT_scaled_size;
+			}
+			output_ptr_offset += compptr.DCT_scaled_size;
+		}
+	}
+
+	if (++(cinfo.output_iMCU_row) < cinfo.total_iMCU_rows)
+		return JPEG_ROW_COMPLETED;
+	return JPEG_SCAN_COMPLETED;
+}
+
+static void post_process_data (jpeg_decompress_struct cinfo,
+				byte[][][] input_buf, int[] input_buf_offset, int[] in_row_group_ctr,
+				int in_row_groups_avail,
+				byte[][] output_buf, int[] out_row_ctr,
+				int out_rows_avail)
+{
+	upsample(cinfo, input_buf, input_buf_offset, in_row_group_ctr, in_row_groups_avail, output_buf, out_row_ctr, out_rows_avail);
+}
+
+static void set_bottom_pointers (jpeg_decompress_struct cinfo)
+/* Change the pointer lists to duplicate the last sample row at the bottom
+ * of the image.	whichptr indicates which xbuffer holds the final iMCU row.
+ * Also sets rowgroups_avail to indicate number of nondummy row groups in row.
+ */
+{
+	jpeg_d_main_controller main = cinfo.main;
+	int ci, i, rgroup, iMCUheight, rows_left;
+	jpeg_component_info compptr;
+	byte[][] xbuf;
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		/* Count sample rows in one iMCU row and in one row group */
+		iMCUheight = compptr.v_samp_factor * compptr.DCT_scaled_size;
+		rgroup = iMCUheight / cinfo.min_DCT_scaled_size;
+		/* Count nondummy sample rows remaining for this component */
+		rows_left = (compptr.downsampled_height % iMCUheight);
+		if (rows_left == 0) rows_left = iMCUheight;
+		/* Count nondummy row groups.	Should get same answer for each component,
+		 * so we need only do it once.
+		 */
+		if (ci == 0) {
+			main.rowgroups_avail = ((rows_left-1) / rgroup + 1);
+		}
+		/* Duplicate the last real sample row rgroup*2 times; this pads out the
+		 * last partial rowgroup and ensures at least one full rowgroup of context.
+		 */
+		xbuf = main.xbuffer[main.whichptr][ci];
+		int xbuf_offset = main.xbuffer_offset[main.whichptr][ci];
+		for (i = 0; i < rgroup * 2; i++) {
+			xbuf[rows_left + i + xbuf_offset] = xbuf[rows_left-1 + xbuf_offset];
+		}
+	}
+}
+
+static void set_wraparound_pointers (jpeg_decompress_struct cinfo)
+/* Set up the "wraparound" pointers at top and bottom of the pointer lists.
+ * This changes the pointer list state from top-of-image to the normal state.
+ */
+{
+	jpeg_d_main_controller main = cinfo.main;
+	int ci, i, rgroup;
+	int M = cinfo.min_DCT_scaled_size;
+	jpeg_component_info compptr;
+	byte[][] xbuf0, xbuf1;
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		rgroup = (compptr.v_samp_factor * compptr.DCT_scaled_size) / cinfo.min_DCT_scaled_size; /* height of a row group of component */
+		xbuf0 = main.xbuffer[0][ci];
+		int xbuf0_offset = main.xbuffer_offset[0][ci];
+		xbuf1 = main.xbuffer[1][ci];
+		int xbuf1_offset = main.xbuffer_offset[1][ci];
+		for (i = 0; i < rgroup; i++) {
+			xbuf0[i - rgroup + xbuf0_offset] = xbuf0[rgroup*(M+1) + i + xbuf0_offset];
+			xbuf1[i - rgroup + xbuf1_offset] = xbuf1[rgroup*(M+1) + i + xbuf1_offset];
+			xbuf0[rgroup*(M+2) + i + xbuf0_offset] = xbuf0[i + xbuf0_offset];
+			xbuf1[rgroup*(M+2) + i + xbuf1_offset] = xbuf1[i + xbuf1_offset];
+		}
+	}
+}
+
+static void process_data_crank_post (jpeg_decompress_struct cinfo,
+	byte[][] output_buf, int[] out_row_ctr,
+	int out_rows_avail)
+{
+	error();
+}
+
+static void process_data_context_main (jpeg_decompress_struct cinfo,
+	byte[][] output_buf, int[] out_row_ctr,
+	int out_rows_avail)
+{
+	jpeg_d_main_controller main = cinfo.main;
+
+	/* Read input data if we haven't filled the main buffer yet */
+	if (! main.buffer_full) {
+		int result;
+		switch (cinfo.coef.decompress_data) {
+			case DECOMPRESS_DATA:
+				result = decompress_data(cinfo, main.xbuffer[main.whichptr], main.xbuffer_offset[main.whichptr]);
+				break;
+			case DECOMPRESS_SMOOTH_DATA:
+				result = decompress_smooth_data(cinfo, main.xbuffer[main.whichptr], main.xbuffer_offset[main.whichptr]);
+				break;
+			case DECOMPRESS_ONEPASS:
+				result = decompress_onepass(cinfo, main.xbuffer[main.whichptr], main.xbuffer_offset[main.whichptr]);
+				break;
+			default: result = 0;
+		}
+		if (result == 0)
+			return;			/* suspension forced, can do nothing more */
+		main.buffer_full = true;	/* OK, we have an iMCU row to work with */
+		main.iMCU_row_ctr++;	/* count rows received */
+	}
+
+	/* Postprocessor typically will not swallow all the input data it is handed
+	 * in one call (due to filling the output buffer first).	Must be prepared
+	 * to exit and restart.	This switch lets us keep track of how far we got.
+	 * Note that each case falls through to the next on successful completion.
+	 */
+	switch (main.context_state) {
+		case CTX_POSTPONED_ROW:
+			/* Call postprocessor using previously set pointers for postponed row */
+			post_process_data (cinfo, main.xbuffer[main.whichptr], main.xbuffer_offset[main.whichptr], main.rowgroup_ctr, main.rowgroups_avail, output_buf, out_row_ctr, out_rows_avail);
+			if (main.rowgroup_ctr[0] < main.rowgroups_avail)
+				return;			/* Need to suspend */
+			main.context_state = CTX_PREPARE_FOR_IMCU;
+			if (out_row_ctr[0] >= out_rows_avail)
+				return;			/* Postprocessor exactly filled output buf */
+			/*FALLTHROUGH*/
+		case CTX_PREPARE_FOR_IMCU:
+			/* Prepare to process first M-1 row groups of this iMCU row */
+			main.rowgroup_ctr[0] = 0;
+			main.rowgroups_avail = (cinfo.min_DCT_scaled_size - 1);
+			/* Check for bottom of image: if so, tweak pointers to "duplicate"
+			 * the last sample row, and adjust rowgroups_avail to ignore padding rows.
+			 */
+			if (main.iMCU_row_ctr == cinfo.total_iMCU_rows)
+				set_bottom_pointers(cinfo);
+			main.context_state = CTX_PROCESS_IMCU;
+			/*FALLTHROUGH*/
+		case CTX_PROCESS_IMCU:
+			/* Call postprocessor using previously set pointers */
+			post_process_data (cinfo, main.xbuffer[main.whichptr], main.xbuffer_offset[main.whichptr], main.rowgroup_ctr, main.rowgroups_avail, output_buf, out_row_ctr, out_rows_avail);
+			if (main.rowgroup_ctr[0] < main.rowgroups_avail)
+				return;			/* Need to suspend */
+			/* After the first iMCU, change wraparound pointers to normal state */
+			if (main.iMCU_row_ctr == 1)
+				set_wraparound_pointers(cinfo);
+			/* Prepare to load new iMCU row using other xbuffer list */
+			main.whichptr ^= 1;	/* 0=>1 or 1=>0 */
+			main.buffer_full = false;
+			/* Still need to process last row group of this iMCU row, */
+			/* which is saved at index M+1 of the other xbuffer */
+			main.rowgroup_ctr[0] = (cinfo.min_DCT_scaled_size + 1);
+			main.rowgroups_avail =	(cinfo.min_DCT_scaled_size + 2);
+			main.context_state = CTX_POSTPONED_ROW;
+	}
+}
+
+static void process_data_simple_main (jpeg_decompress_struct cinfo, byte[][] output_buf, int[] out_row_ctr, int out_rows_avail) {
+	jpeg_d_main_controller main = cinfo.main;
+	int rowgroups_avail;
+
+	/* Read input data if we haven't filled the main buffer yet */
+	if (! main.buffer_full) {
+		int result;
+		switch (cinfo.coef.decompress_data) {
+			case DECOMPRESS_DATA:
+				result = decompress_data(cinfo, main.buffer, main.buffer_offset);
+				break;
+			case DECOMPRESS_SMOOTH_DATA:
+				result = decompress_smooth_data(cinfo, main.buffer, main.buffer_offset);
+				break;
+			case DECOMPRESS_ONEPASS:
+				result = decompress_onepass(cinfo, main.buffer, main.buffer_offset);
+				break;
+			default: result = 0;
+		}
+		if (result == 0)
+			return;			/* suspension forced, can do nothing more */
+		main.buffer_full = true;	/* OK, we have an iMCU row to work with */
+	}
+
+	/* There are always min_DCT_scaled_size row groups in an iMCU row. */
+	rowgroups_avail = cinfo.min_DCT_scaled_size;
+	/* Note: at the bottom of the image, we may pass extra garbage row groups
+	 * to the postprocessor.	The postprocessor has to check for bottom
+	 * of image anyway (at row resolution), so no point in us doing it too.
+	 */
+
+	/* Feed the postprocessor */
+	post_process_data (cinfo, main.buffer, main.buffer_offset, main.rowgroup_ctr, rowgroups_avail, output_buf, out_row_ctr, out_rows_avail);
+
+	/* Has postprocessor consumed all the data yet? If so, mark buffer empty */
+	if (main.rowgroup_ctr[0] >= rowgroups_avail) {
+		main.buffer_full = false;
+		main.rowgroup_ctr[0] = 0;
+	}
+}
+
+static int jpeg_read_scanlines (jpeg_decompress_struct cinfo, byte[][] scanlines, int max_lines) {
+
+	if (cinfo.global_state != DSTATE_SCANNING)
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+	if (cinfo.output_scanline >= cinfo.output_height) {
+//		WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
+		return 0;
+	}
+
+	/* Call progress monitor hook if present */
+//	if (cinfo.progress != NULL) {
+//		cinfo.progress.pass_counter = cast(long) cinfo.output_scanline;
+//		cinfo.progress.pass_limit = cast(long) cinfo.output_height;
+//		(*cinfo.progress.progress_monitor) ((j_common_ptr) cinfo);
+//	}
+
+	/* Process some data */
+	cinfo.row_ctr[0] = 0;
+	switch (cinfo.main.process_data) {
+		case PROCESS_DATA_SIMPLE_MAIN:
+			process_data_simple_main (cinfo, scanlines, cinfo.row_ctr, max_lines);
+			break;
+		case PROCESS_DATA_CONTEXT_MAIN:
+			process_data_context_main (cinfo, scanlines, cinfo.row_ctr, max_lines);
+			break;
+		case PROCESS_DATA_CRANK_POST:
+			process_data_crank_post (cinfo, scanlines, cinfo.row_ctr, max_lines);
+			break;
+		default: error();
+	}
+	cinfo.output_scanline += cinfo.row_ctr[0];
+	return cinfo.row_ctr[0];
+}
+
+
+static bool output_pass_setup (jpeg_decompress_struct cinfo) {
+	if (cinfo.global_state != DSTATE_PRESCAN) {
+		/* First call: do pass setup */
+		prepare_for_output_pass (cinfo);
+		cinfo.output_scanline = 0;
+		cinfo.global_state = DSTATE_PRESCAN;
+	}
+	/* Loop over any required dummy passes */
+	while (cinfo.master.is_dummy_pass) {
+		error();
+//#ifdef QUANT_2PASS_SUPPORTED
+//		/* Crank through the dummy pass */
+//		while (cinfo.output_scanline < cinfo.output_height) {
+//			JDIMENSION last_scanline;
+//			/* Call progress monitor hook if present */
+//			if (cinfo.progress != NULL) {
+//	cinfo.progress.pass_counter = cast(long) cinfo.output_scanline;
+//	cinfo.progress.pass_limit = cast(long) cinfo.output_height;
+//	(*cinfo.progress.progress_monitor) ((j_common_ptr) cinfo);
+//			}
+//			/* Process some data */
+//			last_scanline = cinfo.output_scanline;
+//			(*cinfo.main.process_data) (cinfo, (JSAMPARRAY) NULL,
+//						&cinfo.output_scanline, (JDIMENSION) 0);
+//			if (cinfo.output_scanline == last_scanline)
+//	return FALSE;		/* No progress made, must suspend */
+//		}
+//		/* Finish up dummy pass, and set up for another one */
+//		(*cinfo.master.finish_output_pass) (cinfo);
+//		(*cinfo.master.prepare_for_output_pass) (cinfo);
+//		cinfo.output_scanline = 0;
+//#else
+//		ERREXIT(cinfo, JERR_NOT_COMPILED);
+//#endif /* QUANT_2PASS_SUPPORTED */
+	}
+	/* Ready for application to drive output pass through
+	 * jpeg_read_scanlines or jpeg_read_raw_data.
+	 */
+	cinfo.global_state = cinfo.raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING;
+	return true;
+}
+
+static bool get_dht (jpeg_decompress_struct cinfo)
+/* Process a DHT marker */
+{
+	int length;
+	byte[] bits = new byte[17];
+	byte[] huffval = new byte[256];
+	int i, index, count;
+	JHUFF_TBL htblptr;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	length -= 2;
+
+	while (length > 16) {
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		index = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+//		TRACEMS1(cinfo, 1, JTRC_DHT, index);
+
+		bits[0] = 0;
+		count = 0;
+		for (i = 1; i <= 16; i++) {
+			if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		bits[i] = cinfo.buffer[cinfo.bytes_offset++];
+			count += bits[i] & 0xFF;
+		}
+
+		length -= 1 + 16;
+
+//		TRACEMS8(cinfo, 2, JTRC_HUFFBITS,
+//			 bits[1], bits[2], bits[3], bits[4],
+//			 bits[5], bits[6], bits[7], bits[8]);
+//		TRACEMS8(cinfo, 2, JTRC_HUFFBITS,
+//			 bits[9], bits[10], bits[11], bits[12],
+//			 bits[13], bits[14], bits[15], bits[16]);
+
+		/* Here we just do minimal validation of the counts to avoid walking
+		 * off the end of our table space.	jdhuff.c will check more carefully.
+		 */
+		if (count > 256 || (count) > length)
+			error();
+//			ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+
+		for (i = 0; i < count; i++) {
+	 		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	 		huffval[i] = cinfo.buffer[cinfo.bytes_offset++];
+		}
+
+		length -= count;
+
+		if ((index & 0x10) != 0) {		/* AC table definition */
+			index -= 0x10;
+			htblptr = cinfo.ac_huff_tbl_ptrs[index] = new JHUFF_TBL();
+		} else {			/* DC table definition */
+			htblptr = cinfo.dc_huff_tbl_ptrs[index] = new JHUFF_TBL();
+		}
+
+		if (index < 0 || index >= NUM_HUFF_TBLS)
+			error();
+//			ERREXIT1(cinfo, JERR_DHT_INDEX, index);
+
+        htblptr.bits[0 .. bits.length ] = bits;
+		htblptr.huffval[ 0 .. huffval.length ] = huffval;
+	}
+
+	if (length != 0)
+		error();
+//		ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+	return true;
+}
+
+
+static bool get_dqt (jpeg_decompress_struct cinfo)
+/* Process a DQT marker */
+{
+	int length;
+	int n, i, prec;
+	int tmp;
+	JQUANT_TBL quant_ptr;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	length -= 2;
+
+	while (length > 0) {
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	n = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		prec = n >> 4;
+		n &= 0x0F;
+
+//		TRACEMS2(cinfo, 1, JTRC_DQT, n, prec);
+
+		if (n >= NUM_QUANT_TBLS)
+			error();
+//			ERREXIT1(cinfo, JERR_DQT_INDEX, n);
+
+		if (cinfo.quant_tbl_ptrs[n] == null)
+			cinfo.quant_tbl_ptrs[n] = new JQUANT_TBL();
+		quant_ptr = cinfo.quant_tbl_ptrs[n];
+
+		for (i = 0; i < DCTSIZE2; i++) {
+			if (prec != 0) {
+				if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+				tmp = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+				if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+				tmp |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+			} else {
+					if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+				tmp = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+			}
+			/* We convert the zigzag-order table to natural array order. */
+			quant_ptr.quantval[jpeg_natural_order[i]] = cast(short) tmp;
+		}
+
+//		if (cinfo.err.trace_level >= 2) {
+//			for (i = 0; i < DCTSIZE2; i += 8) {
+//				TRACEMS8(cinfo, 2, JTRC_QUANTVALS,
+//		 			quant_ptr.quantval[i],	 quant_ptr.quantval[i+1],
+//					 quant_ptr.quantval[i+2], quant_ptr.quantval[i+3],
+//					 quant_ptr.quantval[i+4], quant_ptr.quantval[i+5],
+//					 quant_ptr.quantval[i+6], quant_ptr.quantval[i+7]);
+//			}
+//		}
+
+		length -= (DCTSIZE2+1);
+		if (prec != 0) length -= DCTSIZE2;
+	}
+
+	if (length != 0)
+		error();
+//		ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+	return true;
+}
+
+static bool get_dri (jpeg_decompress_struct cinfo)
+/* Process a DRI marker */
+{
+	int length;
+	int tmp;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	if (length != 4)
+	error();
+//		ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	tmp = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	tmp |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+//	TRACEMS1(cinfo, 1, JTRC_DRI, tmp);
+
+	cinfo.restart_interval = tmp;
+
+	return true;
+}
+
+static bool get_dac (jpeg_decompress_struct cinfo)
+/* Process a DAC marker */
+{
+	int length;
+	int index, val;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	length -= 2;
+
+	while (length > 0) {
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		index = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		val = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+		length -= 2;
+
+//		TRACEMS2(cinfo, 1, JTRC_DAC, index, val);
+
+		if (index < 0 || index >= (2*NUM_ARITH_TBLS))
+			error();
+//			ERREXIT1(cinfo, JERR_DAC_INDEX, index);
+
+		if (index >= NUM_ARITH_TBLS) { /* define AC table */
+			cinfo.arith_ac_K[index-NUM_ARITH_TBLS] = cast(byte) val;
+		} else {			/* define DC table */
+			cinfo.arith_dc_L[index] = cast(byte) (val & 0x0F);
+			cinfo.arith_dc_U[index] = cast(byte) (val >> 4);
+			if (cinfo.arith_dc_L[index] > cinfo.arith_dc_U[index])
+				error();
+//	ERREXIT1(cinfo, JERR_DAC_VALUE, val);
+		}
+	}
+
+	if (length != 0)
+		error();
+//		ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+	return true;
+}
+
+
+static bool get_sos (jpeg_decompress_struct cinfo)
+/* Process a SOS marker */
+{
+	int length;
+	int i, ci, n, c, cc;
+	jpeg_component_info compptr = null;
+
+	if (! cinfo.marker.saw_SOF)
+		error();
+//		ERREXIT(cinfo, JERR_SOS_NO_SOF);
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	n = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+//	TRACEMS1(cinfo, 1, JTRC_SOS, n);
+
+	if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN)
+		error();
+//		ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+	cinfo.comps_in_scan = n;
+
+	/* Collect the component-spec parameters */
+
+	for (i = 0; i < n; i++) {
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		cc = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+		for (ci = 0; ci < cinfo.num_components; ci++) {
+			compptr = cinfo.comp_info[ci];
+			if (cc == compptr.component_id)
+				break;
+		}
+
+		if (ci == cinfo.num_components)
+			error();
+//			ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc);
+
+		cinfo.cur_comp_info[i] = compptr;
+		compptr.dc_tbl_no = (c >> 4) & 15;
+		compptr.ac_tbl_no = (c		 ) & 15;
+
+//		TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, compptr.dc_tbl_no, compptr.ac_tbl_no);
+	}
+
+	/* Collect the additional scan parameters Ss, Se, Ah/Al. */
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	cinfo.Ss = c;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	cinfo.Se = c;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	cinfo.Ah = (c >> 4) & 15;
+	cinfo.Al = (c		 ) & 15;
+
+//	TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo.Ss, cinfo.Se, cinfo.Ah, cinfo.Al);
+
+	/* Prepare to scan data & restart markers */
+	cinfo.marker.next_restart_num = 0;
+
+	/* Count another SOS marker */
+	cinfo.input_scan_number++;
+
+	return true;
+}
+
+static bool get_sof (jpeg_decompress_struct cinfo, bool is_prog, bool is_arith) {
+	int length;
+	int c, ci;
+
+	cinfo.progressive_mode = is_prog;
+	cinfo.arith_code = is_arith;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	cinfo.data_precision = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	cinfo.image_height = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	cinfo.image_height |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	cinfo.image_width = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	cinfo.image_width |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	cinfo.num_components = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	length -= 8;
+
+//	TRACEMS4(cinfo, 1, JTRC_SOF, cinfo.unread_marker,
+//		 cast(int) cinfo.image_width, cast(int) cinfo.image_height,
+//		 cinfo.num_components);
+
+	if (cinfo.marker.saw_SOF)
+		error();
+//		ERREXIT(cinfo, JERR_SOF_DUPLICATE);
+
+	/* We don't support files in which the image height is initially specified */
+	/* as 0 and is later redefined by DNL.	As long as we have to check that,	*/
+	/* might as well have a general sanity check. */
+	if (cinfo.image_height <= 0 || cinfo.image_width <= 0 || cinfo.num_components <= 0)
+		error();
+//		ERREXIT(cinfo, JERR_EMPTY_IMAGE);
+
+	if (length != (cinfo.num_components * 3))
+		error();
+//		ERREXIT(cinfo, JERR_BAD_LENGTH);
+
+	if (cinfo.comp_info == null)	/* do only once, even if suspend */
+		cinfo.comp_info = new jpeg_component_info[cinfo.num_components];
+
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		jpeg_component_info compptr = cinfo.comp_info[ci] = new jpeg_component_info();
+		compptr.component_index = ci;
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		compptr.component_id = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		compptr.h_samp_factor = (c >> 4) & 15;
+		compptr.v_samp_factor = (c		 ) & 15;
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		compptr.quant_tbl_no = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+//		TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT,
+//			 compptr.component_id, compptr.h_samp_factor,
+//			 compptr.v_samp_factor, compptr.quant_tbl_no);
+	}
+
+	cinfo.marker.saw_SOF = true;
+
+	return true;
+}
+
+static void sep_upsample (jpeg_decompress_struct cinfo, byte[][][] input_buf, int[] input_buf_offset,
+		int[] in_row_group_ctr, int in_row_groups_avail,
+		byte[][] output_buf, int[] out_row_ctr,	int out_rows_avail)
+{
+	jpeg_upsampler upsample = cinfo.upsample;
+	int ci;
+	jpeg_component_info compptr;
+	int num_rows;
+
+	/* Fill the conversion buffer, if it's empty */
+	if (upsample.next_row_out >= cinfo.max_v_samp_factor) {
+		for (ci = 0; ci < cinfo.num_components; ci++) {
+			compptr = cinfo.comp_info[ci];
+			/* Invoke per-component upsample method.	Notice we pass a POINTER
+			 * to color_buf[ci], so that fullsize_upsample can change it.
+			 */
+			int offset = input_buf_offset[ci] + (in_row_group_ctr[0] * upsample.rowgroup_height[ci]);
+			switch (upsample.methods[ci]) {
+				case NOOP_UPSAMPLE: noop_upsample(cinfo, compptr, input_buf[ci], offset, upsample.color_buf, upsample.color_buf_offset, ci); break;
+				case FULLSIZE_UPSAMPLE: fullsize_upsample(cinfo, compptr, input_buf[ci], offset, upsample.color_buf, upsample.color_buf_offset, ci); break;
+				case H2V1_FANCY_UPSAMPLE: h2v1_fancy_upsample(cinfo, compptr, input_buf[ci], offset, upsample.color_buf, upsample.color_buf_offset, ci); break;
+				case H2V1_UPSAMPLE: h2v1_upsample(cinfo, compptr, input_buf[ci], offset, upsample.color_buf, upsample.color_buf_offset, ci); break;
+				case H2V2_FANCY_UPSAMPLE: h2v2_fancy_upsample(cinfo, compptr, input_buf[ci], offset, upsample.color_buf, upsample.color_buf_offset, ci); break;
+				case H2V2_UPSAMPLE: h2v2_upsample(cinfo, compptr, input_buf[ci], offset, upsample.color_buf, upsample.color_buf_offset, ci); break;
+				case INT_UPSAMPLE: int_upsample(cinfo, compptr, input_buf[ci], offset, upsample.color_buf, upsample.color_buf_offset, ci); break;
+			}
+		}
+		upsample.next_row_out = 0;
+	}
+
+	/* Color-convert and emit rows */
+
+	/* How many we have in the buffer: */
+	num_rows =	(cinfo.max_v_samp_factor - upsample.next_row_out);
+	/* Not more than the distance to the end of the image.	Need this test
+	 * in case the image height is not a multiple of max_v_samp_factor:
+	 */
+	if (num_rows > upsample.rows_to_go)
+		num_rows = upsample.rows_to_go;
+	/* And not more than what the client can accept: */
+	out_rows_avail -= out_row_ctr[0];
+	if (num_rows > out_rows_avail)
+		num_rows = out_rows_avail;
+
+	switch (cinfo.cconvert.color_convert) {
+		case NULL_CONVERT: null_convert (cinfo, upsample.color_buf, upsample.color_buf_offset, upsample.next_row_out, output_buf, out_row_ctr[0], num_rows); break;
+		case GRAYSCALE_CONVERT: grayscale_convert (cinfo, upsample.color_buf, upsample.color_buf_offset, upsample.next_row_out, output_buf, out_row_ctr[0], num_rows); break;
+		case YCC_RGB_CONVERT: ycc_rgb_convert (cinfo, upsample.color_buf, upsample.color_buf_offset, upsample.next_row_out, output_buf, out_row_ctr[0], num_rows); break;
+		case GRAY_RGB_CONVERT: gray_rgb_convert (cinfo, upsample.color_buf, upsample.color_buf_offset, upsample.next_row_out, output_buf, out_row_ctr[0], num_rows); break;
+		case YCCK_CMYK_CONVERT: error(); break;
+	}
+
+	/* Adjust counts */
+	out_row_ctr[0] += num_rows;
+	upsample.rows_to_go -= num_rows;
+	upsample.next_row_out += num_rows;
+	/* When the buffer is emptied, declare this input row group consumed */
+	if (upsample.next_row_out >= cinfo.max_v_samp_factor) {
+		in_row_group_ctr[0]++;
+	}
+}
+
+static void noop_upsample (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	 byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int[] output_data_offset, int output_data_index)
+{
+	output_data_ptr[output_data_index] = null;	/* safety check */
+}
+
+static void fullsize_upsample (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	 byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int[] output_data_offset, int output_data_index)
+{
+	output_data_ptr[output_data_index] = input_data;
+	output_data_offset[output_data_index] = input_data_offset;
+}
+
+static void h2v1_upsample (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	 byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int[] output_data_offset, int output_data_index)
+{
+	byte[][] output_data = output_data_ptr[output_data_index];
+	byte[] inptr, outptr;
+	byte invalue;
+	int outend;
+	int inrow;
+	output_data_offset[output_data_index] = 0;
+
+	for (inrow = 0; inrow < cinfo.max_v_samp_factor; inrow++) {
+		inptr = input_data[inrow+input_data_offset];
+		outptr = output_data[inrow];
+		int inptr_offset = 0, outptr_offset = 0;
+		outend = outptr_offset + cinfo.output_width;
+		while (outptr_offset < outend) {
+			invalue = inptr[inptr_offset++];	/* don't need GETJSAMPLE() here */
+			outptr[outptr_offset++] = invalue;
+			outptr[outptr_offset++] = invalue;
+		}
+	}
+}
+
+static void h2v2_upsample (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int[] output_data_offset, int output_data_index)
+{
+	byte[][] output_data = output_data_ptr[output_data_index];
+	byte[] inptr, outptr;
+	byte invalue;
+	int outend;
+	int inrow, outrow;
+	output_data_offset[output_data_index] = 0;
+
+	inrow = outrow = 0;
+	while (outrow < cinfo.max_v_samp_factor) {
+		inptr = input_data[inrow+input_data_offset];
+		outptr = output_data[outrow];
+		int inptr_offset = 0, outptr_offset = 0;
+		outend = outptr_offset + cinfo.output_width;
+		while (outptr_offset < outend) {
+			invalue = inptr[inptr_offset++];	/* don't need GETJSAMPLE() here */
+			outptr[outptr_offset++] = invalue;
+			outptr[outptr_offset++] = invalue;
+		}
+		jcopy_sample_rows(output_data, outrow, output_data, outrow+1, 1, cinfo.output_width);
+		inrow++;
+		outrow += 2;
+	}
+}
+
+static void h2v1_fancy_upsample (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	 byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int[] output_data_offset, int output_data_index)
+{
+	byte[][] output_data = output_data_ptr[output_data_index];
+	byte[] inptr, outptr;
+	int invalue;
+	int colctr;
+	int inrow;
+	output_data_offset[output_data_index] = 0;
+
+	for (inrow = 0; inrow < cinfo.max_v_samp_factor; inrow++) {
+		inptr = input_data[inrow+input_data_offset];
+		outptr = output_data[inrow];
+		int inptr_offset = 0, outptr_offset = 0;
+		/* Special case for first column */
+		invalue = inptr[inptr_offset++] & 0xFF;
+		outptr[outptr_offset++] = cast(byte) invalue;
+		outptr[outptr_offset++] = cast(byte) ((invalue * 3 + (inptr[inptr_offset] & 0xFF) + 2) >> 2);
+
+		for (colctr = compptr.downsampled_width - 2; colctr > 0; colctr--) {
+			/* General case: 3/4 * nearer pixel + 1/4 * further pixel */
+			invalue = (inptr[inptr_offset++] & 0xFF) * 3;
+			outptr[outptr_offset++] = cast(byte) ((invalue + (inptr[inptr_offset-2] & 0xFF) + 1) >> 2);
+			outptr[outptr_offset++] = cast(byte) ((invalue + (inptr[inptr_offset] & 0xFF) + 2) >> 2);
+		}
+
+		/* Special case for last column */
+		invalue = (inptr[inptr_offset] & 0xFF);
+		outptr[outptr_offset++] = cast(byte) ((invalue * 3 + (inptr[inptr_offset-1] & 0xFF) + 1) >> 2);
+		outptr[outptr_offset++] = cast(byte) invalue;
+	}
+}
+
+static void h2v2_fancy_upsample (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int[] output_data_offset, int output_data_index)
+{
+	byte[][] output_data = output_data_ptr[output_data_index];
+	byte[] inptr0, inptr1, outptr;
+	int thiscolsum, lastcolsum, nextcolsum;
+	int colctr;
+	int inrow, outrow, v;
+	output_data_offset[output_data_index] = 0;
+
+	inrow = outrow = 0;
+	while (outrow < cinfo.max_v_samp_factor) {
+		for (v = 0; v < 2; v++) {
+			/* inptr0 points to nearest input row, inptr1 points to next nearest */
+			inptr0 = input_data[inrow+input_data_offset];
+			if (v == 0)		/* next nearest is row above */
+				inptr1 = input_data[inrow-1+input_data_offset];
+			else			/* next nearest is row below */
+				inptr1 = input_data[inrow+1+input_data_offset];
+			outptr = output_data[outrow++];
+
+			int inptr0_offset = 0, inptr1_offset = 0, outptr_offset = 0;
+
+			/* Special case for first column */
+			thiscolsum = (inptr0[inptr0_offset++] & 0xFF) * 3 + (inptr1[inptr1_offset++] & 0xFF);
+			nextcolsum = (inptr0[inptr0_offset++] & 0xFF) * 3 + (inptr1[inptr1_offset++] & 0xFF);
+			outptr[outptr_offset++] = cast(byte) ((thiscolsum * 4 + 8) >> 4);
+			outptr[outptr_offset++] = cast(byte) ((thiscolsum * 3 + nextcolsum + 7) >> 4);
+			lastcolsum = thiscolsum; thiscolsum = nextcolsum;
+
+			for (colctr = compptr.downsampled_width - 2; colctr > 0; colctr--) {
+				/* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */
+				/* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */
+				nextcolsum = (inptr0[inptr0_offset++] & 0xFF) * 3 + (inptr1[inptr1_offset++] & 0xFF);
+				outptr[outptr_offset++] = cast(byte) ((thiscolsum * 3 + lastcolsum + 8) >> 4);
+				outptr[outptr_offset++] = cast(byte) ((thiscolsum * 3 + nextcolsum + 7) >> 4);
+				lastcolsum = thiscolsum; thiscolsum = nextcolsum;
+			}
+
+			/* Special case for last column */
+			outptr[outptr_offset++] = cast(byte) ((thiscolsum * 3 + lastcolsum + 8) >> 4);
+			outptr[outptr_offset++] = cast(byte) ((thiscolsum * 4 + 7) >> 4);
+		}
+		inrow++;
+	}
+}
+
+static void int_upsample (jpeg_decompress_struct cinfo, jpeg_component_info compptr,
+	 byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int[] output_data_offset, int output_data_index)
+{
+	jpeg_upsampler upsample = cinfo.upsample;
+	byte[][] output_data = output_data_ptr[output_data_index];
+	byte[] inptr, outptr;
+	byte invalue;
+	int h;
+	int outend;
+	int h_expand, v_expand;
+	int inrow, outrow;
+	output_data_offset[output_data_index] = 0;
+
+	h_expand = upsample.h_expand[compptr.component_index];
+	v_expand = upsample.v_expand[compptr.component_index];
+
+	inrow = outrow = 0;
+	while (outrow < cinfo.max_v_samp_factor) {
+		/* Generate one output row with proper horizontal expansion */
+		inptr = input_data[inrow+input_data_offset];
+		int inptr_offset = 0;
+		outptr = output_data[outrow];
+		int outptr_offset = 0;
+		outend = outptr_offset + cinfo.output_width;
+		while (outptr_offset < outend) {
+			invalue = inptr[inptr_offset++];	/* don't need GETJSAMPLE() here */
+			for (h = h_expand; h > 0; h--) {
+				outptr[outptr_offset++] = invalue;
+			}
+		}
+		/* Generate any additional output rows by duplicating the first one */
+		if (v_expand > 1) {
+			jcopy_sample_rows(output_data, outrow, output_data, outrow+1, v_expand-1, cinfo.output_width);
+		}
+		inrow++;
+		outrow += v_expand;
+	}
+}
+
+static void null_convert (jpeg_decompress_struct cinfo,
+	byte[][][] input_buf, int[] input_buf_offset, int input_row,
+	byte[][] output_buf, int output_buf_offset, int num_rows)
+{
+	byte[] inptr, outptr;
+	int count;
+	int num_components = cinfo.num_components;
+	int num_cols = cinfo.output_width;
+	int ci;
+
+	while (--num_rows >= 0) {
+		for (ci = 0; ci < num_components; ci++) {
+			inptr = input_buf[ci][input_row+input_buf_offset[0]];
+			outptr = output_buf[output_buf_offset];
+			/* BGR instead of RGB */
+			int offset = 0;
+			switch (ci) {
+				case 2: offset = RGB_BLUE; break;
+				case 1: offset = RGB_GREEN; break;
+				case 0: offset = RGB_RED; break;
+			}
+			int outptr_offset = offset, inptr_offset = 0;
+			for (count = num_cols; count > 0; count--) {
+				outptr[outptr_offset] = inptr[inptr_offset++];	/* needn't bother with GETJSAMPLE() here */
+				outptr_offset += num_components;
+			}
+		}
+		input_row++;
+		output_buf_offset++;
+	}
+}
+
+static void grayscale_convert (jpeg_decompress_struct cinfo,
+	byte[][][] input_buf, int[] input_buf_offset, int input_row,
+	byte[][] output_buf, int output_buf_offset, int num_rows)
+{
+  jcopy_sample_rows(input_buf[0], input_row+input_buf_offset[0], output_buf, output_buf_offset,
+		    num_rows, cinfo.output_width);
+}
+
+static void gray_rgb_convert (jpeg_decompress_struct cinfo,
+	byte[][][] input_buf, int[] input_buf_offset, int input_row,
+	byte[][] output_buf, int output_buf_offset, int num_rows)
+{
+	byte[] inptr, outptr;
+	int col;
+	int num_cols = cinfo.output_width;
+
+	while (--num_rows >= 0) {
+		inptr = input_buf[0][input_row+++input_buf_offset[0]];
+		outptr = output_buf[output_buf_offset++];
+		int outptr_offset = 0;
+		for (col = 0; col < num_cols; col++) {
+			/* We can dispense with GETJSAMPLE() here */
+			outptr[RGB_RED+outptr_offset] = outptr[RGB_GREEN+outptr_offset] = outptr[RGB_BLUE+outptr_offset] = inptr[col];
+			outptr_offset += RGB_PIXELSIZE;
+		}
+	}
+}
+
+static void ycc_rgb_convert (jpeg_decompress_struct cinfo,
+	byte[][][] input_buf, int[] input_buf_offset, int input_row,
+	byte[][] output_buf, int output_buf_offset, int num_rows)
+{
+	jpeg_color_deconverter cconvert = cinfo.cconvert;
+	int y, cb, cr;
+	byte[] outptr;
+	byte[] inptr0, inptr1, inptr2;
+	int col;
+	int num_cols = cinfo.output_width;
+	/* copy these pointers into registers if possible */
+	byte[] range_limit = cinfo.sample_range_limit;
+	int range_limit_offset = cinfo.sample_range_limit_offset;
+	int[] Crrtab = cconvert.Cr_r_tab;
+	int[] Cbbtab = cconvert.Cb_b_tab;
+	int[] Crgtab = cconvert.Cr_g_tab;
+	int[] Cbgtab = cconvert.Cb_g_tab;
+//		SHIFT_TEMPS
+
+	while (--num_rows >= 0) {
+		inptr0 = input_buf[0][input_row+input_buf_offset[0]];
+		inptr1 = input_buf[1][input_row+input_buf_offset[1]];
+		inptr2 = input_buf[2][input_row+input_buf_offset[2]];
+		input_row++;
+		outptr = output_buf[output_buf_offset++];
+		int outptr_offset = 0;
+		for (col = 0; col < num_cols; col++) {
+			y = (inptr0[col] & 0xFF);
+			cb = (inptr1[col] & 0xFF);
+			cr = (inptr2[col] & 0xFF);
+			/* Range-limiting is essential due to noise introduced by DCT losses. */
+			outptr[outptr_offset + RGB_RED] =	range_limit[y + Crrtab[cr] + range_limit_offset];
+			outptr[outptr_offset + RGB_GREEN] = range_limit[y + ((Cbgtab[cb] + Crgtab[cr]>>SCALEBITS)) + range_limit_offset];
+			outptr[outptr_offset + RGB_BLUE] =	range_limit[y + Cbbtab[cb] + range_limit_offset];
+			outptr_offset += RGB_PIXELSIZE;
+		}
+	}
+}
+
+static bool process_APPn(int n, jpeg_decompress_struct cinfo) {
+	if (n == 0 || n == 14) {
+		return get_interesting_appn(cinfo);
+	}
+	return skip_variable(cinfo);
+}
+
+static bool process_COM(jpeg_decompress_struct cinfo) {
+	return skip_variable(cinfo);
+}
+
+static void skip_input_data (jpeg_decompress_struct cinfo, int num_bytes) {
+	if (num_bytes > 0) {
+		while (num_bytes > cinfo.bytes_in_buffer - cinfo.bytes_offset) {
+			num_bytes -= cinfo.bytes_in_buffer - cinfo.bytes_offset;
+			if (!fill_input_buffer(cinfo)) error();
+			/* note we assume that fill_input_buffer will never return FALSE,
+			 * so suspension need not be handled.
+			 */
+		}
+		cinfo.bytes_offset += num_bytes;
+	}
+}
+
+static bool skip_variable (jpeg_decompress_struct cinfo)
+/* Skip over an unknown or uninteresting variable-length marker */
+{
+	int length;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+
+	length -= 2;
+
+//	TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo.unread_marker, cast(int) length);
+
+	if (length > 0) {
+		skip_input_data (cinfo, length);
+	}
+
+	return true;
+}
+
+static bool get_interesting_appn (jpeg_decompress_struct cinfo)
+/* Process an APP0 or APP14 marker without saving it */
+{
+	int length;
+	byte[] b = new byte[APPN_DATA_LEN];
+	int i, numtoread;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length = (cinfo.buffer[cinfo.bytes_offset++] & 0xFF) << 8;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	length |= cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	length -= 2;
+
+	/* get the interesting part of the marker data */
+	if (length >= APPN_DATA_LEN)
+		numtoread = APPN_DATA_LEN;
+	else if (length > 0)
+		numtoread = length;
+	else
+		numtoread = 0;
+	for (i = 0; i < numtoread; i++) {
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		b[i] = cinfo.buffer[cinfo.bytes_offset++];
+	}
+	length -= numtoread;
+
+	/* process it */
+	switch (cinfo.unread_marker) {
+		case M_APP0:
+			examine_app0(cinfo, b, numtoread, length);
+			break;
+		case M_APP14:
+			examine_app14(cinfo, b, numtoread, length);
+			break;
+		default:
+			/* can't get here unless jpeg_save_markers chooses wrong processor */
+			error();
+//			ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo.unread_marker);
+			break;
+	}
+
+	/* skip any remaining data -- could be lots */
+	if (length > 0)
+		skip_input_data (cinfo, length);
+
+	return true;
+}
+
+static void examine_app0 (jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining)
+/* Examine first few bytes from an APP0.
+ * Take appropriate action if it is a JFIF marker.
+ * datalen is # of bytes at data[], remaining is length of rest of marker data.
+ */
+{
+	int totallen = datalen + remaining;
+
+	if (datalen >= APP0_DATA_LEN &&
+			(data[0] & 0xFF) == 0x4A &&
+			(data[1] & 0xFF) == 0x46 &&
+			(data[2] & 0xFF) == 0x49 &&
+			(data[3] & 0xFF) == 0x46 &&
+			(data[4] & 0xFF) == 0)
+	{
+		/* Found JFIF APP0 marker: save info */
+		cinfo.saw_JFIF_marker = true;
+		cinfo.JFIF_major_version = (data[5]);
+		cinfo.JFIF_minor_version = cast(byte)(data[6] & 0xFF);
+		cinfo.density_unit = cast(byte)(data[7] & 0xFF);
+		cinfo.X_density = cast(short)(((data[8] & 0xFF) << 8) + (data[9] & 0xFF));
+		cinfo.Y_density = cast(short)(((data[10] & 0xFF) << 8) + (data[11] & 0xFF));
+		/* Check version.
+		 * Major version must be 1, anything else signals an incompatible change.
+		 * (We used to treat this as an error, but now it's a nonfatal warning,
+		 * because some bozo at Hijaak couldn't read the spec.)
+		 * Minor version should be 0..2, but process anyway if newer.
+		 */
+		if (cinfo.JFIF_major_version != 1) {
+//			WARNMS2(cinfo, JWRN_JFIF_MAJOR,
+//				cinfo.JFIF_major_version, cinfo.JFIF_minor_version);
+		}
+		/* Generate trace messages */
+//		TRACEMS5(cinfo, 1, JTRC_JFIF,
+//			 cinfo.JFIF_major_version, cinfo.JFIF_minor_version,
+//			 cinfo.X_density, cinfo.Y_density, cinfo.density_unit);
+		/* Validate thumbnail dimensions and issue appropriate messages */
+		if (((data[12] & 0xFF) | (data[13]) & 0xFF) != 0) {
+//			TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL,
+//				 GETJOCTET(data[12]), GETJOCTET(data[13]));
+		}
+		totallen -= APP0_DATA_LEN;
+		if (totallen !=	((data[12] & 0xFF) * (data[13] & 0xFF) * 3)) {
+//			TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, cast(int) totallen);
+		}
+	} else if (datalen >= 6 &&
+			(data[0] & 0xFF) == 0x4A &&
+			(data[1] & 0xFF) == 0x46 &&
+			(data[2] & 0xFF) == 0x58 &&
+			(data[3] & 0xFF) == 0x58 &&
+			(data[4] & 0xFF) == 0)
+	{
+		/* Found JFIF "JFXX" extension APP0 marker */
+		/* The library doesn't actually do anything with these,
+		 * but we try to produce a helpful trace message.
+		 */
+		switch ((data[5]) & 0xFF) {
+			case 0x10:
+//				TRACEMS1(cinfo, 1, JTRC_THUMB_JPEG, cast(int) totallen);
+				break;
+			case 0x11:
+//				TRACEMS1(cinfo, 1, JTRC_THUMB_PALETTE, cast(int) totallen);
+				break;
+			case 0x13:
+//				TRACEMS1(cinfo, 1, JTRC_THUMB_RGB, cast(int) totallen);
+				break;
+			default:
+//				TRACEMS2(cinfo, 1, JTRC_JFIF_EXTENSION, GETJOCTET(data[5]), cast(int) totallen);
+			break;
+		}
+	} else {
+		/* Start of APP0 does not match "JFIF" or "JFXX", or too short */
+//		TRACEMS1(cinfo, 1, JTRC_APP0, cast(int) totallen);
+	}
+}
+
+static void examine_app14 (jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining)
+/* Examine first few bytes from an APP14.
+ * Take appropriate action if it is an Adobe marker.
+ * datalen is # of bytes at data[], remaining is length of rest of marker data.
+ */
+{
+	int /*version, flags0, flags1, */transform;
+
+	if (datalen >= APP14_DATA_LEN &&
+			(data[0] & 0xFF) == 0x41 &&
+			(data[1] & 0xFF) == 0x64 &&
+			(data[2] & 0xFF) == 0x6F &&
+			(data[3] & 0xFF) == 0x62 &&
+			(data[4] & 0xFF) == 0x65)
+	{
+		/* Found Adobe APP14 marker */
+//		version = ((data[5] & 0xFF) << 8) + (data[6] & 0xFF);
+//		flags0 = ((data[7] & 0xFF) << 8) + (data[8] & 0xFF);
+//		flags1 = ((data[9] & 0xFF) << 8) + (data[10] & 0xFF);
+		transform = (data[11] & 0xFF);
+//		TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform);
+		cinfo.saw_Adobe_marker = true;
+		cinfo.Adobe_transform = cast(byte) transform;
+	} else {
+		/* Start of APP14 does not match "Adobe", or too short */
+//		TRACEMS1(cinfo, 1, JTRC_APP14, cast(int) (datalen + remaining));
+	}
+}
+
+static bool get_soi (jpeg_decompress_struct cinfo) /* Process an SOI marker */ {
+	int i;
+
+//	TRACEMS(cinfo, 1, JTRC_SOI);
+
+	if (cinfo.marker.saw_SOI)
+		error();
+//		ERREXIT(cinfo, JERR_SOI_DUPLICATE);
+
+	/* Reset all parameters that are defined to be reset by SOI */
+
+	for (i = 0; i < NUM_ARITH_TBLS; i++) {
+		cinfo.arith_dc_L[i] = 0;
+		cinfo.arith_dc_U[i] = 1;
+		cinfo.arith_ac_K[i] = 5;
+	}
+	cinfo.restart_interval = 0;
+
+	/* Set initial assumptions for colorspace etc */
+
+	cinfo.jpeg_color_space = JCS_UNKNOWN;
+	cinfo.CCIR601_sampling = false; /* Assume non-CCIR sampling??? */
+
+	cinfo.saw_JFIF_marker = false;
+	cinfo.JFIF_major_version = 1; /* set default JFIF APP0 values */
+	cinfo.JFIF_minor_version = 1;
+	cinfo.density_unit = 0;
+	cinfo.X_density = 1;
+	cinfo.Y_density = 1;
+	cinfo.saw_Adobe_marker = false;
+	cinfo.Adobe_transform = 0;
+
+	cinfo.marker.saw_SOI = true;
+
+	return true;
+}
+
+static void jinit_input_controller (jpeg_decompress_struct cinfo)
+{
+	/* Initialize state: can't use reset_input_controller since we don't
+	 * want to try to reset other modules yet.
+	 */
+	jpeg_input_controller inputctl = cinfo.inputctl = new jpeg_input_controller();
+	inputctl.has_multiple_scans = false; /* "unknown" would be better */
+	inputctl.eoi_reached = false;
+	inputctl.inheaders = true;
+}
+
+static void reset_marker_reader (jpeg_decompress_struct cinfo) {
+	jpeg_marker_reader marker = cinfo.marker;
+
+	cinfo.comp_info = null;		/* until allocated by get_sof */
+	cinfo.input_scan_number = 0;		/* no SOS seen yet */
+	cinfo.unread_marker = 0;		/* no pending marker */
+	marker.saw_SOI = false;		/* set internal state too */
+	marker.saw_SOF = false;
+	marker.discarded_bytes = 0;
+//	marker.cur_marker = null;
+}
+
+static void reset_input_controller (jpeg_decompress_struct cinfo) {
+	jpeg_input_controller inputctl = cinfo.inputctl;
+
+	inputctl.has_multiple_scans = false; /* "unknown" would be better */
+	inputctl.eoi_reached = false;
+	inputctl.inheaders = true;
+	/* Reset other modules */
+	reset_marker_reader (cinfo);
+	/* Reset progression state -- would be cleaner if entropy decoder did this */
+	cinfo.coef_bits = null;
+}
+
+static void finish_output_pass (jpeg_decompress_struct cinfo) {
+	jpeg_decomp_master master = cinfo.master;
+
+	if (cinfo.quantize_colors) {
+		error(SWT.ERROR_NOT_IMPLEMENTED);
+//		(*cinfo.cquantize.finish_pass) (cinfo);
+	}
+	master.pass_number++;
+}
+
+static void jpeg_destroy (jpeg_decompress_struct cinfo) {
+	/* We need only tell the memory manager to release everything. */
+	/* NB: mem pointer is NULL if memory mgr failed to initialize. */
+//	if (cinfo.mem != NULL)
+//		(*cinfo.mem.self_destruct) (cinfo);
+//	cinfo.mem = NULL;		/* be safe if jpeg_destroy is called twice */
+	cinfo.global_state = 0;	/* mark it destroyed */
+}
+
+static void jpeg_destroy_decompress (jpeg_decompress_struct cinfo) {
+	jpeg_destroy(cinfo); /* use common routine */
+}
+
+static bool jpeg_input_complete (jpeg_decompress_struct cinfo) {
+	/* Check for valid jpeg object */
+	if (cinfo.global_state < DSTATE_START || cinfo.global_state > DSTATE_STOPPING)
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+	return cinfo.inputctl.eoi_reached;
+}
+
+static bool jpeg_start_output (jpeg_decompress_struct cinfo, int scan_number) {
+	if (cinfo.global_state != DSTATE_BUFIMAGE && cinfo.global_state != DSTATE_PRESCAN)
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+	/* Limit scan number to valid range */
+	if (scan_number <= 0)
+		scan_number = 1;
+	if (cinfo.inputctl.eoi_reached && scan_number > cinfo.input_scan_number)
+		scan_number = cinfo.input_scan_number;
+	cinfo.output_scan_number = scan_number;
+	/* Perform any dummy output passes, and set up for the real pass */
+	return output_pass_setup(cinfo);
+}
+
+static bool jpeg_finish_output (jpeg_decompress_struct cinfo) {
+	if ((cinfo.global_state == DSTATE_SCANNING || cinfo.global_state == DSTATE_RAW_OK) && cinfo.buffered_image) {
+		/* Terminate this pass. */
+		/* We do not require the whole pass to have been completed. */
+		finish_output_pass (cinfo);
+		cinfo.global_state = DSTATE_BUFPOST;
+	} else if (cinfo.global_state != DSTATE_BUFPOST) {
+		/* BUFPOST = repeat call after a suspension, anything else is error */
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+	}
+	/* Read markers looking for SOS or EOI */
+	while (cinfo.input_scan_number <= cinfo.output_scan_number && !cinfo.inputctl.eoi_reached) {
+		if (consume_input (cinfo) == JPEG_SUSPENDED)
+			return false;		/* Suspend, come back later */
+	}
+	cinfo.global_state = DSTATE_BUFIMAGE;
+	return true;
+}
+
+static bool jpeg_finish_decompress (jpeg_decompress_struct cinfo) {
+	if ((cinfo.global_state == DSTATE_SCANNING || cinfo.global_state == DSTATE_RAW_OK) && ! cinfo.buffered_image) {
+		/* Terminate final pass of non-buffered mode */
+		if (cinfo.output_scanline < cinfo.output_height)
+			error();
+//			ERREXIT(cinfo, JERR_TOO_LITTLE_DATA);
+		finish_output_pass (cinfo);
+		cinfo.global_state = DSTATE_STOPPING;
+	} else if (cinfo.global_state == DSTATE_BUFIMAGE) {
+		/* Finishing after a buffered-image operation */
+		cinfo.global_state = DSTATE_STOPPING;
+	} else if (cinfo.global_state != DSTATE_STOPPING) {
+		/* STOPPING = repeat call after a suspension, anything else is error */
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+	}
+	/* Read until EOI */
+	while (! cinfo.inputctl.eoi_reached) {
+		if (consume_input (cinfo) == JPEG_SUSPENDED)
+			return false;		/* Suspend, come back later */
+	}
+	/* Do final cleanup */
+//	(*cinfo.src.term_source) (cinfo);
+	/* We can use jpeg_abort to release memory and reset global_state */
+	jpeg_abort(cinfo);
+	return true;
+}
+
+
+static int jpeg_read_header (jpeg_decompress_struct cinfo, bool require_image) {
+	int retcode;
+
+	if (cinfo.global_state != DSTATE_START && cinfo.global_state != DSTATE_INHEADER)
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+
+	retcode = jpeg_consume_input(cinfo);
+
+	switch (retcode) {
+		case JPEG_REACHED_SOS:
+			retcode = JPEG_HEADER_OK;
+			break;
+		case JPEG_REACHED_EOI:
+			if (require_image)		/* Complain if application wanted an image */
+				error();
+//				ERREXIT(cinfo, JERR_NO_IMAGE);
+			/* Reset to start state; it would be safer to require the application to
+			 * call jpeg_abort, but we can't change it now for compatibility reasons.
+			 * A side effect is to free any temporary memory (there shouldn't be any).
+			 */
+			jpeg_abort(cinfo); /* sets state = DSTATE_START */
+			retcode = JPEG_HEADER_TABLES_ONLY;
+			break;
+		case JPEG_SUSPENDED:
+			/* no work */
+			break;
+	}
+
+	return retcode;
+}
+
+static int dummy_consume_data (jpeg_decompress_struct cinfo) {
+	return JPEG_SUSPENDED;	/* Always indicate nothing was done */
+}
+
+static int consume_data (jpeg_decompress_struct cinfo) {
+	jpeg_d_coef_controller coef = cinfo.coef;
+	int MCU_col_num;	/* index of current MCU within row */
+	int blkn, ci, xindex, yindex, yoffset;
+	int start_col;
+//	short[][][][] buffer = new short[MAX_COMPS_IN_SCAN][][][];
+	short[][] buffer_ptr;
+	jpeg_component_info compptr;
+
+//	/* Align the virtual buffers for the components used in this scan. */
+//	for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+//		compptr = cinfo.cur_comp_info[ci];
+//		buffer[ci] = coef.whole_image[compptr.component_index];
+//		/* Note: entropy decoder expects buffer to be zeroed,
+//		 * but this is handled automatically by the memory manager
+//		 * because we requested a pre-zeroed array.
+//		 */
+//	}
+
+	/* Loop to process one whole iMCU row */
+	for (yoffset = coef.MCU_vert_offset; yoffset < coef.MCU_rows_per_iMCU_row; yoffset++) {
+		for (MCU_col_num = coef.MCU_ctr; MCU_col_num < cinfo.MCUs_per_row; MCU_col_num++) {
+			/* Construct list of pointers to DCT blocks belonging to this MCU */
+			blkn = 0; /* index of current DCT block within MCU */
+			for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+				compptr = cinfo.cur_comp_info[ci];
+				start_col = MCU_col_num * compptr.MCU_width;
+				for (yindex = 0; yindex < compptr.MCU_height; yindex++) {
+//					buffer_ptr = buffer[ci][yindex+yoffset] + start_col;
+					buffer_ptr = coef.whole_image[compptr.component_index][yindex+yoffset+cinfo.input_iMCU_row*compptr.v_samp_factor];
+					int buffer_ptr_offset = start_col;
+					for (xindex = 0; xindex < compptr.MCU_width; xindex++) {
+						coef.MCU_buffer[blkn++] = buffer_ptr[buffer_ptr_offset++];
+					}
+				}
+			}
+			/* Try to fetch the MCU. */
+			if (! cinfo.entropy.decode_mcu (cinfo, coef.MCU_buffer)) {
+				/* Suspension forced; update state counters and exit */
+				coef.MCU_vert_offset = yoffset;
+				coef.MCU_ctr = MCU_col_num;
+				return JPEG_SUSPENDED;
+			}
+		}
+		/* Completed an MCU row, but perhaps not an iMCU row */
+		coef.MCU_ctr = 0;
+	}
+	/* Completed the iMCU row, advance counters for next one */
+	if (++(cinfo.input_iMCU_row) < cinfo.total_iMCU_rows) {
+		coef.start_iMCU_row(cinfo);
+		return JPEG_ROW_COMPLETED;
+	}
+	/* Completed the scan */
+	finish_input_pass (cinfo);
+	return JPEG_SCAN_COMPLETED;
+}
+
+static int consume_input (jpeg_decompress_struct cinfo) {
+	switch (cinfo.inputctl.consume_input) {
+		case COEF_CONSUME_INPUT:
+			 switch (cinfo.coef.consume_data) {
+				case CONSUME_DATA: return consume_data(cinfo);
+				case DUMMY_CONSUME_DATA: return dummy_consume_data(cinfo);
+				default: error();
+			 }
+			 break;
+		case INPUT_CONSUME_INPUT:
+			return consume_markers(cinfo);
+		default:
+			error();
+	}
+	return 0;
+}
+
+static bool fill_input_buffer(jpeg_decompress_struct cinfo) {
+	try {
+		InputStream inputStream = cinfo.inputStream;
+		int nbytes = inputStream.read(cinfo.buffer);
+		if (nbytes <= 0) {
+			if (cinfo.start_of_file)	/* Treat empty input file as fatal error */
+				error();
+//				ERREXIT(cinfo, JERR_INPUT_EMPTY);
+//			WARNMS(cinfo, JWRN_JPEG_EOF);
+			/* Insert a fake EOI marker */
+			cinfo.buffer[0] = cast(byte)0xFF;
+			cinfo.buffer[1] = cast(byte)M_EOI;
+			nbytes = 2;
+		}
+		cinfo.bytes_in_buffer = nbytes;
+		cinfo.bytes_offset = 0;
+		cinfo.start_of_file = false;
+	} catch (IOException e) {
+		error(SWT.ERROR_IO);
+		return false;
+	}
+	return true;
+}
+
+static bool first_marker (jpeg_decompress_struct cinfo) {
+	/* Like next_marker, but used to obtain the initial SOI marker. */
+	/* For this marker, we do not allow preceding garbage or fill; otherwise,
+	 * we might well scan an entire input file before realizing it ain't JPEG.
+	 * If an application wants to process non-JFIF files, it must seek to the
+	 * SOI before calling the JPEG library.
+	 */
+	int c, c2;
+
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+	c2 = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+	if (c != 0xFF || c2 != M_SOI)
+		error();
+//		ERREXIT2(cinfo, JERR_NO_SOI, c, c2);
+
+	cinfo.unread_marker = c2;
+
+	return true;
+}
+
+static bool next_marker (jpeg_decompress_struct cinfo) {
+	int c;
+
+	for (;;) {
+		if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+		c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		/* Skip any non-FF bytes.
+		 * This may look a bit inefficient, but it will not occur in a valid file.
+		 * We sync after each discarded byte so that a suspending data source
+		 * can discard the byte from its buffer.
+		 */
+		while (c != 0xFF) {
+			cinfo.marker.discarded_bytes++;
+			if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+			c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		}
+		/* This loop swallows any duplicate FF bytes.	Extra FFs are legal as
+		 * pad bytes, so don't count them in discarded_bytes.	We assume there
+		 * will not be so many consecutive FF bytes as to overflow a suspending
+		 * data source's input buffer.
+		 */
+		do {
+			 if (cinfo.bytes_offset == cinfo.bytes_in_buffer) fill_input_buffer(cinfo);
+				c = cinfo.buffer[cinfo.bytes_offset++] & 0xFF;
+		} while (c == 0xFF);
+		if (c != 0)
+			break;			/* found a valid marker, exit loop */
+		/* Reach here if we found a stuffed-zero data sequence (FF/00).
+		 * Discard it and loop back to try again.
+		 */
+		cinfo.marker.discarded_bytes += 2;
+	}
+
+	if (cinfo.marker.discarded_bytes != 0) {
+//		WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo.marker.discarded_bytes, c);
+		cinfo.marker.discarded_bytes = 0;
+	}
+
+	cinfo.unread_marker = c;
+
+	return true;
+}
+
+static int read_markers (jpeg_decompress_struct cinfo) {
+	/* Outer loop repeats once for each marker. */
+	for (;;) {
+		/* Collect the marker proper, unless we already did. */
+		/* NB: first_marker() enforces the requirement that SOI appear first. */
+		if (cinfo.unread_marker == 0) {
+			if (! cinfo.marker.saw_SOI) {
+				if (! first_marker(cinfo))
+					return JPEG_SUSPENDED;
+				} else {
+					if (! next_marker(cinfo))
+						return JPEG_SUSPENDED;
+				}
+		}
+		/* At this point cinfo.unread_marker contains the marker code and the
+		 * input point is just past the marker proper, but before any parameters.
+		 * A suspension will cause us to return with this state still true.
+		 */
+		switch (cinfo.unread_marker) {
+			case M_SOI:
+				if (! get_soi(cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_SOF0:		/* Baseline */
+			case M_SOF1:		/* Extended sequential, Huffman */
+				if (! get_sof(cinfo, false, false))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_SOF2:		/* Progressive, Huffman */
+				if (! get_sof(cinfo, true, false))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_SOF9:		/* Extended sequential, arithmetic */
+				if (! get_sof(cinfo, false, true))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_SOF10:		/* Progressive, arithmetic */
+				if (! get_sof(cinfo, true, true))
+					return JPEG_SUSPENDED;
+				break;
+
+			/* Currently unsupported SOFn types */
+			case M_SOF3:		/* Lossless, Huffman */
+			case M_SOF5:		/* Differential sequential, Huffman */
+			case M_SOF6:		/* Differential progressive, Huffman */
+			case M_SOF7:		/* Differential lossless, Huffman */
+			case M_JPG:			/* Reserved for JPEG extensions */
+			case M_SOF11:		/* Lossless, arithmetic */
+			case M_SOF13:		/* Differential sequential, arithmetic */
+			case M_SOF14:		/* Differential progressive, arithmetic */
+			case M_SOF15:		/* Differential lossless, arithmetic */
+				error();
+//				ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo.unread_marker);
+				break;
+
+			case M_SOS:
+				if (! get_sos(cinfo))
+					return JPEG_SUSPENDED;
+				cinfo.unread_marker = 0;	/* processed the marker */
+				return JPEG_REACHED_SOS;
+
+			case M_EOI:
+//				TRACEMS(cinfo, 1, JTRC_EOI);
+				cinfo.unread_marker = 0;	/* processed the marker */
+				return JPEG_REACHED_EOI;
+
+			case M_DAC:
+				if (! get_dac(cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_DHT:
+				if (! get_dht(cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_DQT:
+				if (! get_dqt(cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_DRI:
+				if (! get_dri(cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_APP0:
+			case M_APP1:
+			case M_APP2:
+			case M_APP3:
+			case M_APP4:
+			case M_APP5:
+			case M_APP6:
+			case M_APP7:
+			case M_APP8:
+			case M_APP9:
+			case M_APP10:
+			case M_APP11:
+			case M_APP12:
+			case M_APP13:
+			case M_APP14:
+			case M_APP15:
+				if (! process_APPn(cinfo.unread_marker - M_APP0, cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_COM:
+				if (! process_COM(cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			case M_RST0:		/* these are all parameterless */
+			case M_RST1:
+			case M_RST2:
+			case M_RST3:
+			case M_RST4:
+			case M_RST5:
+			case M_RST6:
+			case M_RST7:
+			case M_TEM:
+//				TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo.unread_marker);
+				break;
+
+			case M_DNL:			/* Ignore DNL ... perhaps the wrong thing */
+				if (! skip_variable(cinfo))
+					return JPEG_SUSPENDED;
+				break;
+
+			default:			/* must be DHP, EXP, JPGn, or RESn */
+				/* For now, we treat the reserved markers as fatal errors since they are
+				 * likely to be used to signal incompatible JPEG Part 3 extensions.
+				 * Once the JPEG 3 version-number marker is well defined, this code
+				 * ought to change!
+				 */
+				error();
+ //		 		ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo.unread_marker);
+				break;
+		}
+		/* Successfully processed marker, so reset state variable */
+		cinfo.unread_marker = 0;
+	} /* end loop */
+}
+
+static long jdiv_round_up (long a, long b)
+/* Compute a/b rounded up to next integer, ie, ceil(a/b) */
+/* Assumes a >= 0, b > 0 */
+{
+	return (a + b - 1) / b;
+}
+
+static void initial_setup (jpeg_decompress_struct cinfo)
+/* Called once, when first SOS marker is reached */
+{
+	int ci;
+	jpeg_component_info compptr;
+
+	/* Make sure image isn't bigger than I can handle */
+	if (cinfo.image_height >	JPEG_MAX_DIMENSION || cinfo.image_width > JPEG_MAX_DIMENSION)
+		error();
+//		ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION);
+
+	/* For now, precision must match compiled-in value... */
+	if (cinfo.data_precision != BITS_IN_JSAMPLE)
+		error(" [data precision=" ~ to!(char[])(cinfo.data_precision) ~ "]");
+//		ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo.data_precision);
+
+	/* Check that number of components won't exceed internal array sizes */
+	if (cinfo.num_components > MAX_COMPONENTS)
+		error();
+//		ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo.num_components, MAX_COMPONENTS);
+
+	/* Compute maximum sampling factors; check factor validity */
+	cinfo.max_h_samp_factor = 1;
+	cinfo.max_v_samp_factor = 1;
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		if (compptr.h_samp_factor<=0 || compptr.h_samp_factor>MAX_SAMP_FACTOR || compptr.v_samp_factor<=0 || compptr.v_samp_factor>MAX_SAMP_FACTOR)
+			error();
+//			ERREXIT(cinfo, JERR_BAD_SAMPLING);
+		cinfo.max_h_samp_factor = Math.max(cinfo.max_h_samp_factor, compptr.h_samp_factor);
+		cinfo.max_v_samp_factor = Math.max(cinfo.max_v_samp_factor, compptr.v_samp_factor);
+	}
+
+	/* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE.
+	 * In the full decompressor, this will be overridden by jdmaster.c;
+	 * but in the transcoder, jdmaster.c is not used, so we must do it here.
+	 */
+	cinfo.min_DCT_scaled_size = DCTSIZE;
+
+	/* Compute dimensions of components */
+	for (ci = 0; ci < cinfo.num_components; ci++) {
+		compptr = cinfo.comp_info[ci];
+		compptr.DCT_scaled_size = DCTSIZE;
+		/* Size in DCT blocks */
+		compptr.width_in_blocks = cast(int)jdiv_round_up(cast(long) cinfo.image_width * cast(long) compptr.h_samp_factor, (cinfo.max_h_samp_factor * DCTSIZE));
+		compptr.height_in_blocks = cast(int)jdiv_round_up(cast(long) cinfo.image_height * cast(long) compptr.v_samp_factor, (cinfo.max_v_samp_factor * DCTSIZE));
+		/* downsampled_width and downsampled_height will also be overridden by
+		 * jdmaster.c if we are doing full decompression.	The transcoder library
+		 * doesn't use these values, but the calling application might.
+		 */
+		/* Size in samples */
+		compptr.downsampled_width = cast(int)jdiv_round_up(cast(long) cinfo.image_width * cast(long) compptr.h_samp_factor,	cinfo.max_h_samp_factor);
+		compptr.downsampled_height = cast(int)jdiv_round_up(cast(long) cinfo.image_height * cast(long) compptr.v_samp_factor, cinfo.max_v_samp_factor);
+		/* Mark component needed, until color conversion says otherwise */
+		compptr.component_needed = true;
+		/* Mark no quantization table yet saved for component */
+		compptr.quant_table = null;
+	}
+
+	/* Compute number of fully interleaved MCU rows. */
+	cinfo.total_iMCU_rows = cast(int)jdiv_round_up( cinfo.image_height, (cinfo.max_v_samp_factor*DCTSIZE));
+
+	/* Decide whether file contains multiple scans */
+	if (cinfo.comps_in_scan < cinfo.num_components || cinfo.progressive_mode)
+		cinfo.inputctl.has_multiple_scans = true;
+	else
+		cinfo.inputctl.has_multiple_scans = false;
+}
+
+
+static void per_scan_setup (jpeg_decompress_struct cinfo)
+/* Do computations that are needed before processing a JPEG scan */
+/* cinfo.comps_in_scan and cinfo.cur_comp_info[] were set from SOS marker */
+{
+	int ci, mcublks, tmp = 0;
+	jpeg_component_info compptr;
+
+	if (cinfo.comps_in_scan == 1) {
+
+		/* Noninterleaved (single-component) scan */
+		compptr = cinfo.cur_comp_info[0];
+
+		/* Overall image size in MCUs */
+		cinfo.MCUs_per_row = compptr.width_in_blocks;
+		cinfo.MCU_rows_in_scan = compptr.height_in_blocks;
+
+		/* For noninterleaved scan, always one block per MCU */
+		compptr.MCU_width = 1;
+		compptr.MCU_height = 1;
+		compptr.MCU_blocks = 1;
+		compptr.MCU_sample_width = compptr.DCT_scaled_size;
+		compptr.last_col_width = 1;
+		/* For noninterleaved scans, it is convenient to define last_row_height
+		 * as the number of block rows present in the last iMCU row.
+		 */
+		tmp = (compptr.height_in_blocks % compptr.v_samp_factor);
+		if (tmp == 0) tmp = compptr.v_samp_factor;
+		compptr.last_row_height = tmp;
+
+		/* Prepare array describing MCU composition */
+		cinfo.blocks_in_MCU = 1;
+		cinfo.MCU_membership[0] = 0;
+
+	} else {
+
+		/* Interleaved (multi-component) scan */
+		if (cinfo.comps_in_scan <= 0 || cinfo.comps_in_scan > MAX_COMPS_IN_SCAN)
+			error();
+//			ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo.comps_in_scan, MAX_COMPS_IN_SCAN);
+
+		/* Overall image size in MCUs */
+		cinfo.MCUs_per_row = cast(int)jdiv_round_up( cinfo.image_width, (cinfo.max_h_samp_factor*DCTSIZE));
+		cinfo.MCU_rows_in_scan = cast(int)jdiv_round_up( cinfo.image_height, (cinfo.max_v_samp_factor*DCTSIZE));
+
+		cinfo.blocks_in_MCU = 0;
+
+		for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+			compptr = cinfo.cur_comp_info[ci];
+			/* Sampling factors give # of blocks of component in each MCU */
+			compptr.MCU_width = compptr.h_samp_factor;
+			compptr.MCU_height = compptr.v_samp_factor;
+			compptr.MCU_blocks = compptr.MCU_width * compptr.MCU_height;
+			compptr.MCU_sample_width = compptr.MCU_width * compptr.DCT_scaled_size;
+			/* Figure number of non-dummy blocks in last MCU column & row */
+			tmp = (compptr.width_in_blocks % compptr.MCU_width);
+			if (tmp == 0) tmp = compptr.MCU_width;
+			compptr.last_col_width = tmp;
+			tmp = (compptr.height_in_blocks % compptr.MCU_height);
+			if (tmp == 0) tmp = compptr.MCU_height;
+			compptr.last_row_height = tmp;
+			/* Prepare array describing MCU composition */
+			mcublks = compptr.MCU_blocks;
+			if (cinfo.blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU)
+				error();
+//	ERREXIT(cinfo, JERR_BAD_MCU_SIZE);
+			while (mcublks-- > 0) {
+				cinfo.MCU_membership[cinfo.blocks_in_MCU++] = ci;
+			}
+		}
+
+	}
+}
+
+static void latch_quant_tables (jpeg_decompress_struct cinfo) {
+	int ci, qtblno;
+	jpeg_component_info compptr;
+	JQUANT_TBL qtbl;
+
+	for (ci = 0; ci < cinfo.comps_in_scan; ci++) {
+		compptr = cinfo.cur_comp_info[ci];
+		/* No work if we already saved Q-table for this component */
+		if (compptr.quant_table != null)
+			continue;
+		/* Make sure specified quantization table is present */
+		qtblno = compptr.quant_tbl_no;
+		if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || cinfo.quant_tbl_ptrs[qtblno] == null)
+			error();
+//			ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno);
+		/* OK, save away the quantization table */
+		qtbl = new JQUANT_TBL();
+        qtbl.quantval[] = cinfo.quant_tbl_ptrs[qtblno].quantval[ 0 .. qtbl.quantval.length ];
+		qtbl.sent_table = cinfo.quant_tbl_ptrs[qtblno].sent_table;
+		compptr.quant_table = qtbl;
+	}
+}
+
+static void jpeg_make_d_derived_tbl (jpeg_decompress_struct cinfo, bool isDC, int tblno, d_derived_tbl dtbl) {
+	JHUFF_TBL htbl;
+	int p, i = 0, l, si, numsymbols;
+	int lookbits, ctr;
+	byte[] huffsize = new byte[257];
+	int[] huffcode = new int[257];
+	int code;
+
+	/* Note that huffsize[] and huffcode[] are filled in code-length order,
+	 * paralleling the order of the symbols themselves in htbl.huffval[].
+	 */
+
+	/* Find the input Huffman table */
+	if (tblno < 0 || tblno >= NUM_HUFF_TBLS)
+		error();
+//		ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno);
+	htbl = isDC ? cinfo.dc_huff_tbl_ptrs[tblno] : cinfo.ac_huff_tbl_ptrs[tblno];
+	if (htbl == null)
+		error();
+//		ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno);
+
+	/* Allocate a workspace if we haven't already done so. */
+	dtbl.pub = htbl;		/* fill in back link */
+
+	/* Figure C.1: make table of Huffman code length for each symbol */
+
+	p = 0;
+	for (l = 1; l <= 16; l++) {
+		i = htbl.bits[l] & 0xFF;
+		if (i < 0 || p + i > 256)	/* protect against table overrun */
+			error();
+//			ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+		while (i-- != 0)
+			huffsize[p++] = cast(byte) l;
+	}
+	huffsize[p] = 0;
+	numsymbols = p;
+
+	/* Figure C.2: generate the codes themselves */
+	/* We also validate that the counts represent a legal Huffman code tree. */
+
+	code = 0;
+	si = huffsize[0];
+	p = 0;
+	while ((huffsize[p]) != 0) {
+		while (( huffsize[p]) == si) {
+			huffcode[p++] = code;
+			code++;
+		}
+		/* code is now 1 more than the last code used for codelength si; but
+		 * it must still fit in si bits, since no code is allowed to be all ones.
+		 */
+		if (( code) >= (( 1) << si))
+			error();
+//			ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+		code <<= 1;
+		si++;
+	}
+
+	/* Figure F.15: generate decoding tables for bit-sequential decoding */
+
+	p = 0;
+	for (l = 1; l <= 16; l++) {
+		if ((htbl.bits[l] & 0xFF) != 0) {
+			/* valoffset[l] = huffval[] index of 1st symbol of code length l,
+			 * minus the minimum code of length l
+			 */
+			dtbl.valoffset[l] =	p -	huffcode[p];
+			p += (htbl.bits[l] & 0xFF);
+			dtbl.maxcode[l] = huffcode[p-1]; /* maximum code of length l */
+		} else {
+			dtbl.maxcode[l] = -1;	/* -1 if no codes of this length */
+		}
+	}
+	dtbl.maxcode[17] = 0xFFFFF; /* ensures jpeg_huff_decode terminates */
+
+	/* Compute lookahead tables to speed up decoding.
+	 * First we set all the table entries to 0, indicating "too long";
+	 * then we iterate through the Huffman codes that are short enough and
+	 * fill in all the entries that correspond to bit sequences starting
+	 * with that code.
+	 */
+
+	for (int j = 0; j < dtbl.look_nbits.length; j++) {
+		dtbl.look_nbits[j] = 0;
+	}
+
+	p = 0;
+	for (l = 1; l <= HUFF_LOOKAHEAD; l++) {
+		for (i = 1; i <= (htbl.bits[l] & 0xFF); i++, p++) {
+			/* l = current code's length, p = its index in huffcode[] & huffval[]. */
+			/* Generate left-justified code followed by all possible bit sequences */
+			lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l);
+			for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) {
+				dtbl.look_nbits[lookbits] = l;
+				dtbl.look_sym[lookbits] = htbl.huffval[p];
+				lookbits++;
+			}
+		}
+	}
+
+	/* Validate symbols as being reasonable.
+	 * For AC tables, we make no check, but accept all byte values 0..255.
+	 * For DC tables, we require the symbols to be in range 0..15.
+	 * (Tighter bounds could be applied depending on the data depth and mode,
+	 * but this is sufficient to ensure safe decoding.)
+	 */
+	if (isDC) {
+		for (i = 0; i < numsymbols; i++) {
+			int sym = htbl.huffval[i] & 0xFF;
+			if (sym < 0 || sym > 15)
+				error();
+//				ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
+		}
+	}
+}
+
+static void start_input_pass (jpeg_decompress_struct cinfo) {
+	per_scan_setup(cinfo);
+	latch_quant_tables(cinfo);
+	cinfo.entropy.start_pass(cinfo);
+	cinfo.coef.start_input_pass (cinfo);
+	cinfo.inputctl.consume_input = COEF_CONSUME_INPUT;
+}
+
+static void finish_input_pass (jpeg_decompress_struct cinfo) {
+	cinfo.inputctl.consume_input = INPUT_CONSUME_INPUT;
+}
+
+static int consume_markers (jpeg_decompress_struct cinfo) {
+	jpeg_input_controller inputctl = cinfo.inputctl;
+	int val;
+
+	if (inputctl.eoi_reached) /* After hitting EOI, read no further */
+		return JPEG_REACHED_EOI;
+
+	val = read_markers (cinfo);
+
+	switch (val) {
+	case JPEG_REACHED_SOS:	/* Found SOS */
+		if (inputctl.inheaders) {	/* 1st SOS */
+			initial_setup(cinfo);
+			inputctl.inheaders = false;
+			/* Note: start_input_pass must be called by jdmaster.c
+			 * before any more input can be consumed.	jdapimin.c is
+			 * responsible for enforcing this sequencing.
+			 */
+		} else {			/* 2nd or later SOS marker */
+			if (! inputctl.has_multiple_scans)
+				error();
+//				ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */
+			start_input_pass(cinfo);
+		}
+		break;
+	case JPEG_REACHED_EOI:	/* Found EOI */
+		inputctl.eoi_reached = true;
+		if (inputctl.inheaders) {	/* Tables-only datastream, apparently */
+			if (cinfo.marker.saw_SOF)
+				error();
+//				ERREXIT(cinfo, JERR_SOF_NO_SOS);
+		} else {
+			/* Prevent infinite loop in coef ctlr's decompress_data routine
+			 * if user set output_scan_number larger than number of scans.
+			 */
+			if (cinfo.output_scan_number > cinfo.input_scan_number)
+				cinfo.output_scan_number = cinfo.input_scan_number;
+		}
+		break;
+	case JPEG_SUSPENDED:
+		break;
+	}
+
+	return val;
+}
+
+static void default_decompress_parms (jpeg_decompress_struct cinfo) {
+	/* Guess the input colorspace, and set output colorspace accordingly. */
+	/* (Wish JPEG committee had provided a real way to specify this...) */
+	/* Note application may override our guesses. */
+	switch (cinfo.num_components) {
+		case 1:
+			cinfo.jpeg_color_space = JCS_GRAYSCALE;
+			cinfo.out_color_space = JCS_GRAYSCALE;
+			break;
+
+		case 3:
+			if (cinfo.saw_JFIF_marker) {
+				cinfo.jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */
+			} else if (cinfo.saw_Adobe_marker) {
+				switch (cinfo.Adobe_transform) {
+					case 0:
+						cinfo.jpeg_color_space = JCS_RGB;
+						break;
+					case 1:
+						cinfo.jpeg_color_space = JCS_YCbCr;
+						break;
+					default:
+//						WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo.Adobe_transform);
+						cinfo.jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */
+					break;
+				}
+			} else {
+				/* Saw no special markers, try to guess from the component IDs */
+				int cid0 = cinfo.comp_info[0].component_id;
+				int cid1 = cinfo.comp_info[1].component_id;
+				int cid2 = cinfo.comp_info[2].component_id;
+
+				if (cid0 == 1 && cid1 == 2 && cid2 == 3)
+					cinfo.jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */
+				else if (cid0 == 82 && cid1 == 71 && cid2 == 66)
+					cinfo.jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */
+				else {
+//					TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2);
+					cinfo.jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */
+				}
+			}
+			/* Always guess RGB is proper output colorspace. */
+			cinfo.out_color_space = JCS_RGB;
+			break;
+
+		case 4:
+			if (cinfo.saw_Adobe_marker) {
+				switch (cinfo.Adobe_transform) {
+					case 0:
+						cinfo.jpeg_color_space = JCS_CMYK;
+						break;
+					case 2:
+						cinfo.jpeg_color_space = JCS_YCCK;
+						break;
+					default:
+//						WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo.Adobe_transform);
+						cinfo.jpeg_color_space = JCS_YCCK; /* assume it's YCCK */
+						break;
+				}
+			} else {
+				/* No special markers, assume straight CMYK. */
+				cinfo.jpeg_color_space = JCS_CMYK;
+			}
+			cinfo.out_color_space = JCS_CMYK;
+			break;
+
+		default:
+			cinfo.jpeg_color_space = JCS_UNKNOWN;
+			cinfo.out_color_space = JCS_UNKNOWN;
+			break;
+	}
+
+	/* Set defaults for other decompression parameters. */
+	cinfo.scale_num = 1;		/* 1:1 scaling */
+	cinfo.scale_denom = 1;
+	cinfo.output_gamma = 1.0;
+	cinfo.buffered_image = false;
+	cinfo.raw_data_out = false;
+	cinfo.dct_method = JDCT_DEFAULT;
+	cinfo.do_fancy_upsampling = true;
+	cinfo.do_block_smoothing = true;
+	cinfo.quantize_colors = false;
+	/* We set these in case application only sets quantize_colors. */
+	cinfo.dither_mode = JDITHER_FS;
+	cinfo.two_pass_quantize = true;
+	cinfo.desired_number_of_colors = 256;
+	cinfo.colormap = null;
+	/* Initialize for no mode change in buffered-image mode. */
+	cinfo.enable_1pass_quant = false;
+	cinfo.enable_external_quant = false;
+	cinfo.enable_2pass_quant = false;
+}
+
+static void init_source(jpeg_decompress_struct cinfo) {
+	cinfo.buffer = new byte[INPUT_BUFFER_SIZE];
+	cinfo.bytes_in_buffer = 0;
+	cinfo.bytes_offset = 0;
+	cinfo.start_of_file = true;
+}
+
+static int jpeg_consume_input (jpeg_decompress_struct cinfo) {
+	int retcode = JPEG_SUSPENDED;
+
+	/* NB: every possible DSTATE value should be listed in this switch */
+	switch (cinfo.global_state) {
+	case DSTATE_START:
+		/* Start-of-datastream actions: reset appropriate modules */
+		reset_input_controller(cinfo);
+		/* Initialize application's data source module */
+		init_source (cinfo);
+		cinfo.global_state = DSTATE_INHEADER;
+		/*FALLTHROUGH*/
+	case DSTATE_INHEADER:
+		retcode = consume_input(cinfo);
+		if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */
+			/* Set up default parameters based on header data */
+			default_decompress_parms(cinfo);
+			/* Set global state: ready for start_decompress */
+			cinfo.global_state = DSTATE_READY;
+		}
+		break;
+	case DSTATE_READY:
+		/* Can't advance past first SOS until start_decompress is called */
+		retcode = JPEG_REACHED_SOS;
+		break;
+	case DSTATE_PRELOAD:
+	case DSTATE_PRESCAN:
+	case DSTATE_SCANNING:
+	case DSTATE_RAW_OK:
+	case DSTATE_BUFIMAGE:
+	case DSTATE_BUFPOST:
+	case DSTATE_STOPPING:
+		retcode = consume_input (cinfo);
+		break;
+	default:
+		error();
+//		ERREXIT1(cinfo, JERR_BAD_STATE, cinfo.global_state);
+	}
+	return retcode;
+}
+
+
+static void jpeg_abort (jpeg_decompress_struct cinfo) {
+//	int pool;
+//
+//	/* Releasing pools in reverse order might help avoid fragmentation
+//	 * with some (brain-damaged) malloc libraries.
+//	 */
+//	for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) {
+//		(*cinfo.mem.free_pool) (cinfo, pool);
+//	}
+
+	/* Reset overall state for possible reuse of object */
+	if (cinfo.is_decompressor) {
+		cinfo.global_state = DSTATE_START;
+		/* Try to keep application from accessing now-deleted marker list.
+		 * A bit kludgy to do it here, but this is the most central place.
+		 */
+//		((j_decompress_ptr) cinfo).marker_list = null;
+	} else {
+		cinfo.global_state = CSTATE_START;
+	}
+}
+
+
+static bool isFileFormat(LEDataInputStream stream) {
+	try {
+		byte[] buffer = new byte[2];
+		stream.read(buffer);
+		stream.unread(buffer);
+		return (buffer[0] & 0xFF) == 0xFF && (buffer[1] & 0xFF) == M_SOI;
+	} catch (TracedException e) {
+		return false;
+	}
+}
+
+static ImageData[] loadFromByteStream(InputStream inputStream, ImageLoader loader) {
+	jpeg_decompress_struct cinfo = new jpeg_decompress_struct();
+	cinfo.inputStream = inputStream;
+	jpeg_create_decompress(cinfo);
+	jpeg_read_header(cinfo, true);
+	cinfo.buffered_image = cinfo.progressive_mode && loader.hasListeners();
+	jpeg_start_decompress(cinfo);
+	PaletteData palette = null;
+	switch (cinfo.out_color_space) {
+		case JCS_RGB:
+			palette = new PaletteData(0xFF, 0xFF00, 0xFF0000);
+			break;
+		case JCS_GRAYSCALE:
+			RGB[] colors = new RGB[256];
+			for (int i = 0; i < colors.length; i++) {
+				colors[i] = new RGB(i, i, i);
+			}
+			palette = new PaletteData(colors);
+			break;
+		default:
+			error();
+	}
+	int scanlinePad = 4;
+	int row_stride = (((cinfo.output_width * cinfo.out_color_components * 8 + 7) / 8) + (scanlinePad - 1)) / scanlinePad * scanlinePad;
+	byte[][] buffer = new byte[][]( 1, row_stride );
+	byte[] data = new byte[row_stride * cinfo.output_height];
+	ImageData imageData = ImageData.internal_new(
+			cinfo.output_width, cinfo.output_height, palette.isDirect ? 24 : 8, palette, scanlinePad, data,
+			0, null, null, -1, -1, SWT.IMAGE_JPEG, 0, 0, 0, 0);
+	if (cinfo.buffered_image) {
+		bool done;
+		do {
+			int incrementCount = cinfo.input_scan_number - 1;
+			jpeg_start_output(cinfo, cinfo.input_scan_number);
+			while (cinfo.output_scanline < cinfo.output_height) {
+				int offset = row_stride * cinfo.output_scanline;
+				jpeg_read_scanlines(cinfo, buffer, 1);
+                data[ offset .. offset+row_stride ] = buffer[0][ 0 .. row_stride ];
+			}
+			jpeg_finish_output(cinfo);
+			loader.notifyListeners(new ImageLoaderEvent(loader, cast(ImageData)imageData.clone(), incrementCount, done = jpeg_input_complete(cinfo)));
+		} while (!done);
+	} else {
+		while (cinfo.output_scanline < cinfo.output_height) {
+			int offset = row_stride * cinfo.output_scanline;
+			jpeg_read_scanlines(cinfo, buffer, 1);
+            data[ offset .. offset+row_stride ] = buffer[0][ 0 .. row_stride ];
+		}
+	}
+	jpeg_finish_decompress(cinfo);
+	jpeg_destroy_decompress(cinfo);
+	return [imageData];
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGEndOfImage.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGEndOfImage;
+
+import dwt.internal.image.JPEGFixedSizeSegment;
+import dwt.internal.image.JPEGFileFormat;
+import dwt.internal.image.LEDataInputStream;
+
+final class JPEGEndOfImage : JPEGFixedSizeSegment {
+
+	public this() {
+		super();
+	}
+
+	public this(byte[] reference) {
+		super(reference);
+	}
+
+	public int signature() {
+		return JPEGFileFormat.EOI;
+	}
+
+	public int fixedSize() {
+		return 2;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGFileFormat.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,1891 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved.  This source file is made available under the terms contained in the README file
+ * accompanying this program.  The README file should be located in the about_files directory of the
+ * plug-in that contains this source file.
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.JPEGFileFormat;
+
+import dwt.SWT;
+import dwt.internal.image.JPEGFrameHeader;
+import dwt.internal.image.JPEGScanHeader;
+import dwt.internal.image.JPEGHuffmanTable;
+import dwt.internal.image.JPEGAppn;
+import dwt.internal.image.JPEGSegment;
+import dwt.internal.image.FileFormat;
+import dwt.internal.image.JPEGComment;
+import dwt.internal.image.JPEGArithmeticConditioningTable;
+import dwt.internal.image.JPEGRestartInterval;
+import dwt.internal.image.JPEGQuantizationTable;
+import dwt.internal.image.JPEGStartOfImage;
+import dwt.internal.image.JPEGDecoder;
+import dwt.internal.image.JPEGEndOfImage;
+
+import dwt.graphics.RGB;
+import dwt.graphics.PaletteData;
+
+import tango.core.Exception;
+
+final class JPEGFileFormat : FileFormat {
+	int restartInterval;
+	JPEGFrameHeader frameHeader;
+	int imageWidth, imageHeight;
+	int interleavedMcuCols, interleavedMcuRows;
+	int maxV, maxH;
+	bool progressive;
+	int samplePrecision;
+	int nComponents;
+	int[][] frameComponents;
+	int[] componentIds;
+	byte[][] imageComponents;
+	int[] dataUnit;
+	int[][][] dataUnits;
+	int[] precedingDCs;
+	JPEGScanHeader scanHeader;
+	byte[] dataBuffer;
+	int currentBitCount;
+	int bufferCurrentPosition;
+	int restartsToGo;
+	int nextRestartNumber;
+	JPEGHuffmanTable[] acHuffmanTables;
+	JPEGHuffmanTable[] dcHuffmanTables;
+	int[][] quantizationTables;
+	int currentByte;
+	int encoderQFactor = 75;
+	int eobrun = 0;
+	/* JPEGConstants */
+	public static const int DCTSIZE = 8;
+	public static const int DCTSIZESQR = 64;
+	/* JPEGFixedPointConstants */
+	public static const int FIX_0_899976223 = 7373;
+	public static const int FIX_1_961570560 = 16069;
+	public static const int FIX_2_053119869 = 16819;
+	public static const int FIX_0_298631336 = 2446;
+	public static const int FIX_1_847759065 = 15137;
+	public static const int FIX_1_175875602 = 9633;
+	public static const int FIX_3_072711026 = 25172;
+	public static const int FIX_0_765366865 = 6270;
+	public static const int FIX_2_562915447 = 20995;
+	public static const int FIX_0_541196100 = 4433;
+	public static const int FIX_0_390180644 = 3196;
+	public static const int FIX_1_501321110 = 12299;
+	/* JPEGMarkerCodes */
+	public static const int APP0  = 0xFFE0;
+	public static const int APP15 = 0xFFEF;
+	public static const int COM   = 0xFFFE;
+	public static const int DAC   = 0xFFCC;
+	public static const int DHP   = 0xFFDE;
+	public static const int DHT   = 0xFFC4;
+	public static const int DNL   = 0xFFDC;
+	public static const int DRI   = 0xFFDD;
+	public static const int DQT   = 0xFFDB;
+	public static const int EOI   = 0xFFD9;
+	public static const int EXP   = 0xFFDF;
+	public static const int JPG   = 0xFFC8;
+	public static const int JPG0  = 0xFFF0;
+	public static const int JPG13 = 0xFFFD;
+	public static const int RST0  = 0xFFD0;
+	public static const int RST1  = 0xFFD1;
+	public static const int RST2  = 0xFFD2;
+	public static const int RST3  = 0xFFD3;
+	public static const int RST4  = 0xFFD4;
+	public static const int RST5  = 0xFFD5;
+	public static const int RST6  = 0xFFD6;
+	public static const int RST7  = 0xFFD7;
+	public static const int SOF0  = 0xFFC0;
+	public static const int SOF1  = 0xFFC1;
+	public static const int SOF2  = 0xFFC2;
+	public static const int SOF3  = 0xFFC3;
+	public static const int SOF5  = 0xFFC5;
+	public static const int SOF6  = 0xFFC6;
+	public static const int SOF7  = 0xFFC7;
+	public static const int SOF9  = 0xFFC9;
+	public static const int SOF10 = 0xFFCA;
+	public static const int SOF11 = 0xFFCB;
+	public static const int SOF13 = 0xFFCD;
+	public static const int SOF14 = 0xFFCE;
+	public static const int SOF15 = 0xFFCF;
+	public static const int SOI   = 0xFFD8;
+	public static const int SOS   = 0xFFDA;
+	public static const int TEM   = 0xFF01;
+	/* JPEGFrameComponentParameterConstants */
+	public static const int TQI	= 0;
+	public static const int HI	= 1;
+	public static const int VI	= 2;
+	public static const int CW	= 3;
+	public static const int CH	= 4;
+	/* JPEGScanComponentParameterConstants */
+	public static const int DC	= 0;
+	public static const int AC	= 1;
+	/* JFIF Component Constants */
+	public static const int ID_Y		= 1 - 1;
+	public static const int ID_CB	= 2 - 1;
+	public static const int ID_CR	= 3 - 1;
+	public static const RGB[] RGB16;
+	public static const int[] ExtendTest = [
+		0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
+		4096, 8192, 16384, 32768, 65536, 131072, 262144
+	];
+	public static const int[] ExtendOffset = [
+		0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047,
+		-4095, -8191, -16383, -32767, -65535, -131071, -262143
+	];
+	public static const int[] ZigZag8x8 = [
+		0, 1, 8, 16, 9, 2, 3, 10,
+		17, 24, 32, 25, 18, 11, 4, 5,
+		12, 19, 26, 33, 40, 48, 41, 34,
+		27, 20, 13, 6, 7, 14, 21, 28,
+		35, 42, 49, 56, 57, 50, 43, 36,
+		29, 22, 15, 23, 30, 37, 44, 51,
+		58, 59, 52, 45, 38, 31, 39, 46,
+		53, 60, 61, 54, 47, 55, 62, 63
+	];
+
+	public static int[] CrRTable, CbBTable, CrGTable, CbGTable;
+	public static int[] RYTable, GYTable, BYTable,
+		RCbTable, GCbTable, BCbTable, RCrTable, GCrTable, BCrTable, NBitsTable;
+	static this(){
+		initialize();
+        RGB16 = [
+            new RGB(0,0,0),
+            new RGB(0x80,0,0),
+            new RGB(0,0x80,0),
+            new RGB(0x80,0x80,0),
+            new RGB(0,0,0x80),
+            new RGB(0x80,0,0x80),
+            new RGB(0,0x80,0x80),
+            new RGB(0xC0,0xC0,0xC0),
+            new RGB(0x80,0x80,0x80),
+            new RGB(0xFF,0,0),
+            new RGB(0,0xFF,0),
+            new RGB(0xFF,0xFF,0),
+            new RGB(0,0,0xFF),
+            new RGB(0xFF,0,0xFF),
+            new RGB(0,0xFF,0xFF),
+            new RGB(0xFF,0xFF,0xFF)
+        ];
+	}
+void compress(ImageData image, byte[] dataYComp, byte[] dataCbComp, byte[] dataCrComp) {
+	int srcWidth = image.width;
+	int srcHeight = image.height;
+	int vhFactor = maxV * maxH;
+	int[] frameComponent;
+	imageComponents = new byte[][](nComponents);
+	for (int i = 0; i < nComponents; i++) {
+		frameComponent = frameComponents[componentIds[i]];
+		imageComponents[i] = new byte[frameComponent[CW] * frameComponent[CH]];
+	}
+	frameComponent = frameComponents[componentIds[ID_Y]];
+	for (int yPos = 0; yPos < srcHeight; yPos++) {
+		int srcOfs = yPos * srcWidth;
+		int dstOfs = yPos * frameComponent[CW];
+        imageComponents[ID_Y][ dstOfs .. dstOfs+srcWidth] = dataYComp[ srcOfs .. srcOfs+srcWidth ];
+	}
+	frameComponent = frameComponents[componentIds[ID_CB]];
+	for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
+		int destRowIndex = yPos * frameComponent[CW];
+		for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
+			int sum = 0;
+			for (int iv = 0; iv < maxV; iv++) {
+				int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
+				for (int ih = 0; ih < maxH; ih++) {
+					sum += dataCbComp[srcIndex + ih] & 0xFF;
+				}
+			}
+			imageComponents[ID_CB][destRowIndex + xPos] = cast(byte)(sum / vhFactor);
+		}
+	}
+	frameComponent = frameComponents[componentIds[ID_CR]];
+	for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
+		int destRowIndex = yPos * frameComponent[CW];
+		for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
+			int sum = 0;
+			for (int iv = 0; iv < maxV; iv++) {
+				int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
+				for (int ih = 0; ih < maxH; ih++) {
+					sum += dataCrComp[srcIndex + ih] & 0xFF;
+				}
+			}
+			imageComponents[ID_CR][destRowIndex + xPos] = cast(byte)(sum / vhFactor);
+		}
+	}
+	for (int iComp = 0; iComp < nComponents; iComp++) {
+		byte[] imageComponent = imageComponents[iComp];
+		frameComponent = frameComponents[componentIds[iComp]];
+		int hFactor = frameComponent[HI];
+		int vFactor = frameComponent[VI];
+		int componentWidth = frameComponent[CW];
+		int componentHeight = frameComponent[CH];
+		int compressedWidth = srcWidth / (maxH / hFactor);
+		int compressedHeight = srcHeight / (maxV / vFactor);
+		if (compressedWidth < componentWidth) {
+			int delta = componentWidth - compressedWidth;
+			for (int yPos = 0; yPos < compressedHeight; yPos++) {
+				int dstOfs = ((yPos + 1) * componentWidth - delta);
+				int dataValue = imageComponent[dstOfs - 1] & 0xFF;
+				for (int i = 0; i < delta; i++) {
+					imageComponent[dstOfs + i] = cast(byte)dataValue;
+				}
+			}
+		}
+		if (compressedHeight < componentHeight) {
+			int srcOfs = (compressedHeight - 1) * componentWidth;
+			for (int yPos = compressedHeight; yPos <= componentHeight; yPos++) {
+				int dstOfs = (yPos - 1) * componentWidth;
+                imageComponent[dstOfs .. dstOfs+componentWidth]  = imageComponent[srcOfs .. srcOfs+componentWidth];
+			}
+		}
+	}
+}
+void convert4BitRGBToYCbCr(ImageData image) {
+	RGB[] rgbs = image.getRGBs();
+	int paletteSize = rgbs.length;
+	byte[] yComp = new byte[paletteSize];
+	byte[] cbComp = new byte[paletteSize];
+	byte[] crComp = new byte[paletteSize];
+	int srcWidth = image.width;
+	int srcHeight = image.height;
+	for (int i = 0; i < paletteSize; i++) {
+		RGB color = rgbs[i];
+		int r = color.red;
+		int g = color.green;
+		int b = color.blue;
+		int n = RYTable[r] + GYTable[g] + BYTable[b];
+		yComp[i] = cast(byte)(n >> 16);
+		if ((n < 0) && ((n & 0xFFFF) != 0)) yComp[i]--;
+		n = RCbTable[r] + GCbTable[g] + BCbTable[b];
+		cbComp[i] = cast(byte)(n >> 16);
+		if ((n < 0) && ((n & 0xFFFF) != 0)) cbComp[i]--;
+		n = RCrTable[r] + GCrTable[g] + BCrTable[b];
+		crComp[i] = cast(byte)(n >> 16);
+		if ((n < 0) && ((n & 0xFFFF) != 0)) crComp[i]--;
+	}
+	int bSize = srcWidth * srcHeight;
+	byte[] dataYComp = new byte[bSize];
+	byte[] dataCbComp = new byte[bSize];
+	byte[] dataCrComp = new byte[bSize];
+	byte[] origData = image.data;
+	int bytesPerLine = image.bytesPerLine;
+	int maxScanlineByte = srcWidth >> 1;
+	for (int yPos = 0; yPos < srcHeight; yPos++) {
+		for (int xPos = 0; xPos < maxScanlineByte; xPos++) {
+			int srcIndex = yPos * bytesPerLine + xPos;
+			int dstIndex = yPos * srcWidth + (xPos * 2);
+			int value2 = origData[srcIndex] & 0xFF;
+			int value1 = value2 >> 4;
+			value2 &= 0x0F;
+			dataYComp[dstIndex] = yComp[value1];
+			dataCbComp[dstIndex] = cbComp[value1];
+			dataCrComp[dstIndex] = crComp[value1];
+			dataYComp[dstIndex + 1] = yComp[value2];
+			dataCbComp[dstIndex + 1] = cbComp[value2];
+			dataCrComp[dstIndex + 1] = crComp[value2];
+		}
+	}
+	compress(image, dataYComp, dataCbComp, dataCrComp);
+}
+void convert8BitRGBToYCbCr(ImageData image) {
+	RGB[] rgbs = image.getRGBs();
+	int paletteSize = rgbs.length;
+	byte[] yComp = new byte[paletteSize];
+	byte[] cbComp = new byte[paletteSize];
+	byte[] crComp = new byte[paletteSize];
+	int srcWidth = image.width;
+	int srcHeight = image.height;
+	for (int i = 0; i < paletteSize; i++) {
+		RGB color = rgbs[i];
+		int r = color.red;
+		int g = color.green;
+		int b = color.blue;
+		int n = RYTable[r] + GYTable[g] + BYTable[b];
+		yComp[i] = cast(byte)(n >> 16);
+		if ((n < 0) && ((n & 0xFFFF) != 0)) yComp[i]--;
+		n = RCbTable[r] + GCbTable[g] + BCbTable[b];
+		cbComp[i] = cast(byte)(n >> 16);
+		if ((n < 0) && ((n & 0xFFFF) != 0)) cbComp[i]--;
+		n = RCrTable[r] + GCrTable[g] + BCrTable[b];
+		crComp[i] = cast(byte)(n >> 16);
+		if ((n < 0) && ((n & 0xFFFF) != 0)) crComp[i]--;
+	}
+	int dstWidth = image.width;
+	int dstHeight = srcHeight;
+	int stride = ((srcWidth + 3) >> 2) << 2;
+	int bSize = dstWidth * dstHeight;
+	byte[] dataYComp = new byte[bSize];
+	byte[] dataCbComp = new byte[bSize];
+	byte[] dataCrComp = new byte[bSize];
+	byte[] origData = image.data;
+	for (int yPos = 0; yPos < srcHeight; yPos++) {
+		int srcRowIndex = yPos * stride;
+		int dstRowIndex = yPos * dstWidth;
+		for (int xPos = 0; xPos < srcWidth; xPos++) {
+			int value = origData[srcRowIndex + xPos] & 0xFF;
+			int dstIndex = dstRowIndex + xPos;
+			dataYComp[dstIndex] = yComp[value];
+			dataCbComp[dstIndex] = cbComp[value];
+			dataCrComp[dstIndex] = crComp[value];
+		}
+	}
+	compress(image, dataYComp, dataCbComp, dataCrComp);
+}
+byte[] convertCMYKToRGB() {
+	/* Unsupported CMYK format. Answer an empty byte array. */
+	return new byte[0];
+}
+void convertImageToYCbCr(ImageData image) {
+	switch (image.depth) {
+		case 4:
+			convert4BitRGBToYCbCr(image);
+			return;
+		case 8:
+			convert8BitRGBToYCbCr(image);
+			return;
+		case 16:
+		case 24:
+		case 32:
+			convertMultiRGBToYCbCr(image);
+			return;
+		default:
+			SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+	}
+	return;
+}
+void convertMultiRGBToYCbCr(ImageData image) {
+	int srcWidth = image.width;
+	int srcHeight = image.height;
+	int bSize = srcWidth * srcHeight;
+	byte[] dataYComp = new byte[bSize];
+	byte[] dataCbComp = new byte[bSize];
+	byte[] dataCrComp = new byte[bSize];
+	PaletteData palette = image.palette;
+	int[] buffer = new int[srcWidth];
+	if (palette.isDirect) {
+		int redMask = palette.redMask;
+		int greenMask = palette.greenMask;
+		int blueMask = palette.blueMask;
+		int redShift = palette.redShift;
+		int greenShift = palette.greenShift;
+		int blueShift = palette.blueShift;
+		for (int yPos = 0; yPos < srcHeight; yPos++) {
+			image.getPixels(0, yPos, srcWidth, buffer, 0);
+			int dstRowIndex = yPos * srcWidth;
+			for (int xPos = 0; xPos < srcWidth; xPos++) {
+				int pixel = buffer[xPos];
+				int dstDataIndex = dstRowIndex + xPos;
+				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;
+				dataYComp[dstDataIndex] = cast(byte)((RYTable[r] + GYTable[g] + BYTable[b]) >> 16);
+				dataCbComp[dstDataIndex] = cast(byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) >> 16);
+				dataCrComp[dstDataIndex] = cast(byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) >> 16);
+			}
+		}
+	} else {
+		for (int yPos = 0; yPos < srcHeight; yPos++) {
+			image.getPixels(0, yPos, srcWidth, buffer, 0);
+			int dstRowIndex = yPos * srcWidth;
+			for (int xPos = 0; xPos < srcWidth; xPos++) {
+				int pixel = buffer[xPos];
+				int dstDataIndex = dstRowIndex + xPos;
+				RGB rgb = palette.getRGB(pixel);
+				int r = rgb.red;
+				int g = rgb.green;
+				int b = rgb.blue;
+				dataYComp[dstDataIndex] = cast(byte)((RYTable[r] + GYTable[g] + BYTable[b]) >> 16);
+				dataCbComp[dstDataIndex] = cast(byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) >> 16);
+				dataCrComp[dstDataIndex] = cast(byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) >> 16);
+			}
+		}
+	}
+	compress(image, dataYComp, dataCbComp, dataCrComp);
+}
+byte[] convertYToRGB() {
+	int compWidth = frameComponents[componentIds[ID_Y]][CW];
+	int bytesPerLine = (((imageWidth * 8 + 7) / 8) + 3) / 4 * 4;
+	byte[] data = new byte[bytesPerLine * imageHeight];
+	byte[] yComp = imageComponents[ID_Y];
+	int destIndex = 0;
+	for (int i = 0; i < imageHeight; i++) {
+		int srcIndex = i * compWidth;
+		for (int j = 0; j < bytesPerLine; j++) {
+			int y = yComp[srcIndex] & 0xFF;
+			if (y < 0) {
+				y = 0;
+			} else {
+				if (y > 255) y = 255;
+			}
+			if (j >= imageWidth) {
+				y = 0;
+			}
+			data[destIndex] = cast(byte)y;
+			srcIndex++;
+			destIndex++;
+		}
+	}
+	return data;
+}
+byte[] convertYCbCrToRGB() {
+	/**
+	 * Convert existing image components into an RGB format.
+	 * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
+	 * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
+	 * The conversion equations to be implemented are therefore
+	 * 	R = Y                + 1.40200 * Cr
+	 * 	G = Y - 0.34414 * Cb - 0.71414 * Cr
+	 * 	B = Y + 1.77200 * Cb
+	 * where Cb and Cr represent the incoming values less MAXJSAMPLE/2.
+	 * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
+	 *
+	 * To avoid floating-point arithmetic, we represent the fractional constants
+	 * as integers scaled up by 2^16 (about 4 digits precision); we have to divide
+	 * the products by 2^16, with appropriate rounding, to get the correct answer.
+	 * Notice that Y, being an integral input, does not contribute any fraction
+	 * so it need not participate in the rounding.
+	 *
+	 * For even more speed, we avoid doing any multiplications in the inner loop
+	 * by precalculating the constants times Cb and Cr for all possible values.
+	 * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
+	 * for 12-bit samples it is still acceptable.  It's not very reasonable for
+	 * 16-bit samples, but if you want lossless storage you shouldn't be changing
+	 * colorspace anyway.
+	 * The Cr=>R and Cb=>B values can be rounded to integers in advance; the
+	 * values for the G calculation are left scaled up, since we must add them
+	 * together before rounding.
+	 */
+	int bSize = imageWidth * imageHeight * nComponents;
+	byte[] rgbData = new byte[bSize];
+	int destIndex = 0;
+	expandImageComponents();
+	byte[] yComp = imageComponents[ID_Y];
+	byte[] cbComp = imageComponents[ID_CB];
+	byte[] crComp = imageComponents[ID_CR];
+	int compWidth = frameComponents[componentIds[ID_Y]][CW];
+	for (int v = 0; v < imageHeight; v++) {
+		int srcIndex = v * compWidth;
+		for (int i = 0; i < imageWidth; i++) {
+			int y = yComp[srcIndex] & 0xFF;
+			int cb = cbComp[srcIndex] & 0xFF;
+			int cr = crComp[srcIndex] & 0xFF;
+			int r = y + CrRTable[cr];
+			int g = y + ((CbGTable[cb] + CrGTable[cr]) >> 16);
+			int b = y + CbBTable[cb];
+			if (r < 0) {
+				r = 0;
+			} else {
+				if (r > 255) r = 255;
+			}
+			if (g < 0) {
+				g = 0;
+			} else {
+				if (g > 255) g = 255;
+			}
+			if (b < 0) {
+				b = 0;
+			} else {
+				if (b > 255) b = 255;
+			}
+			rgbData[destIndex] = cast(byte)b;
+			rgbData[destIndex + 1] = cast(byte)g;
+			rgbData[destIndex + 2] = cast(byte)r;
+			destIndex += 3;
+			srcIndex++;
+		}
+	}
+	return rgbData;
+}
+void decodeACCoefficients(int[] dataUnit, int iComp) {
+	int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+	JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+	int k = 1;
+	while (k < 64) {
+		int rs = decodeUsingTable(acTable);
+		int r = rs >> 4;
+		int s = rs & 0xF;
+		if (s == 0) {
+			if (r == 15) {
+				k += 16;
+			} else {
+				break;
+			}
+		} else {
+			k += r;
+			int bits = receive(s);
+			dataUnit[ZigZag8x8[k]] = extendBy(bits, s);
+			k++;
+		}
+	}
+}
+void decodeACFirstCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
+	if (eobrun > 0) {
+		eobrun--;
+		return;
+	}
+	int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+	JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+	int k = start;
+	while (k <= end) {
+		int rs = decodeUsingTable(acTable);
+		int r = rs >> 4;
+		int s = rs & 0xF;
+		if (s == 0) {
+			if (r == 15) {
+				k += 16;
+			} else {
+				eobrun = (1 << r) + receive(r) - 1;
+				break;
+			}
+		} else {
+			k += r;
+			int bits = receive(s);
+			dataUnit[ZigZag8x8[k]] = extendBy(bits, s) << approxBit;
+			k++;
+		}
+	}
+}
+void decodeACRefineCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
+	int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+	JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+	int k = start;
+	while (k <= end) {
+		if (eobrun > 0) {
+			while (k <= end) {
+				int zzIndex = ZigZag8x8[k];
+				if (dataUnit[zzIndex] != 0) {
+					dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+				}
+				k++;
+			}
+			eobrun--;
+		} else {
+			int rs = decodeUsingTable(acTable);
+			int r = rs >> 4;
+			int s = rs & 0xF;
+			if (s == 0) {
+				if (r == 15) {
+					int zeros = 0;
+					while (zeros < 16 && k <= end) {
+						int zzIndex = ZigZag8x8[k];
+						if (dataUnit[zzIndex] != 0) {
+							dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+						} else {
+							zeros++;
+						}
+						k++;
+					}
+				} else {
+					eobrun = (1 << r) + receive(r);
+				}
+			} else {
+				int bit = receive(s);
+				int zeros = 0;
+				int zzIndex = ZigZag8x8[k];
+				while ((zeros < r || dataUnit[zzIndex] != 0) && k <= end) {
+					if (dataUnit[zzIndex] != 0) {
+						dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
+					} else {
+						zeros++;
+					}
+					k++;
+					zzIndex = ZigZag8x8[k];
+				}
+				if (bit != 0) {
+					dataUnit[zzIndex] = 1 << approxBit;
+				} else {
+					dataUnit[zzIndex] = -1 << approxBit;
+				}
+				k++;
+			}
+		}
+	}
+}
+int refineAC(int ac, int approxBit) {
+	if (ac > 0) {
+		int bit = nextBit();
+		if (bit != 0) {
+			ac += 1 << approxBit;
+		}
+	} else if (ac < 0) {
+		int bit = nextBit();
+		if (bit != 0) {
+			ac += -1 << approxBit;
+		}
+	}
+	return ac;
+}
+void decodeDCCoefficient(int[] dataUnit, int iComp, bool first, int approxBit) {
+	int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
+	JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
+	int lastDC = 0;
+	if (progressive && !first) {
+		int bit = nextBit();
+		lastDC = dataUnit[0] + (bit << approxBit);
+	} else {
+		lastDC = precedingDCs[iComp];
+		int nBits = decodeUsingTable(dcTable);
+		if (nBits != 0) {
+			int bits = receive(nBits);
+			int diff = extendBy(bits, nBits);
+			lastDC += diff;
+			precedingDCs[iComp] = lastDC;
+		}
+		if (progressive) {
+			lastDC = lastDC << approxBit;
+		}
+	}
+	dataUnit[0] = lastDC;
+}
+void dequantize(int[] dataUnit, int iComp) {
+	int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
+	for (int i = 0; i < dataUnit.length; i++) {
+		int zzIndex = ZigZag8x8[i];
+		dataUnit[zzIndex] = dataUnit[zzIndex] * qTable[i];
+	}
+}
+byte[] decodeImageComponents() {
+	if (nComponents == 3) { // compIds 1, 2, 3
+		return convertYCbCrToRGB();
+	}
+//	if (nComponents == 3) { // compIds 1, 4, 5
+//		Unsupported CMYK format.
+//		return convertYIQToRGB();
+//	}
+	if (nComponents == 4) {
+		return convertCMYKToRGB();
+	}
+	return convertYToRGB();
+}
+void decodeMCUAtXAndY(int xmcu, int ymcu, int nComponentsInScan, bool first, int start, int end, int approxBit) {
+	for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
+		int scanComponent = iComp;
+		while (scanHeader.componentParameters[componentIds[scanComponent]] == null) {
+			scanComponent++;
+		}
+		int[] frameComponent = frameComponents[componentIds[scanComponent]];
+		int hi = frameComponent[HI];
+		int vi = frameComponent[VI];
+		if (nComponentsInScan == 1) {
+			hi = 1;
+			vi = 1;
+		}
+		int compWidth = frameComponent[CW];
+		for (int ivi = 0; ivi < vi; ivi++) {
+			for (int ihi = 0; ihi < hi; ihi++) {
+				if (progressive) {
+					// Progressive: First scan - create a new data unit.
+					// Subsequent scans - refine the existing data unit.
+					int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
+					dataUnit = dataUnits[scanComponent][index];
+					if (dataUnit == null) {
+						dataUnit = new int[64];
+						dataUnits[scanComponent][index] = dataUnit;
+					}
+				} else {
+					// Sequential: Clear and reuse the data unit buffer.
+					for (int i = 0; i < dataUnit.length; i++) {
+						dataUnit[i] = 0;
+					}
+				}
+				if (!progressive || scanHeader.isDCProgressiveScan()) {
+					decodeDCCoefficient(dataUnit, scanComponent, first, approxBit);
+				}
+				if (!progressive) {
+					decodeACCoefficients(dataUnit, scanComponent);
+				} else {
+					if (scanHeader.isACProgressiveScan()) {
+						if (first) {
+							decodeACFirstCoefficients(dataUnit, scanComponent, start, end, approxBit);
+						} else {
+							decodeACRefineCoefficients(dataUnit, scanComponent, start, end, approxBit);
+						}
+					}
+					if (loader.hasListeners()) {
+						// Dequantization, IDCT, up-sampling and color conversion
+						// are done on a copy of the coefficient data in order to
+						// display the image incrementally.
+                        dataUnit.length = 64;
+					}
+				}
+				if (!progressive || (progressive && loader.hasListeners())) {
+					dequantize(dataUnit, scanComponent);
+					inverseDCT(dataUnit);
+					storeData(dataUnit, scanComponent, xmcu, ymcu, hi, ihi, vi, ivi);
+				}
+			}
+		}
+	}
+}
+void decodeScan() {
+	if (progressive && !scanHeader.verifyProgressiveScan()) {
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+	int nComponentsInScan = scanHeader.getNumberOfImageComponents();
+	int mcuRowsInScan = interleavedMcuRows;
+	int mcusPerRow = interleavedMcuCols;
+	if (nComponentsInScan == 1) {
+		// Non-interleaved.
+		int scanComponent = 0;
+		while (scanHeader.componentParameters[componentIds[scanComponent]] == null) {
+			scanComponent++;
+		}
+		int[] frameComponent = frameComponents[componentIds[scanComponent]];
+		int hi = frameComponent[HI];
+		int vi = frameComponent[VI];
+		int mcuWidth = DCTSIZE * maxH / hi;
+		int mcuHeight = DCTSIZE * maxV / vi;
+		mcusPerRow = (imageWidth + mcuWidth - 1) / mcuWidth;
+		mcuRowsInScan = (imageHeight + mcuHeight - 1) / mcuHeight;
+	}
+	bool first = scanHeader.isFirstScan();
+	int start = scanHeader.getStartOfSpectralSelection();
+	int end = scanHeader.getEndOfSpectralSelection();
+	int approxBit = scanHeader.getApproxBitPositionLow();
+	restartsToGo = restartInterval;
+	nextRestartNumber = 0;
+	for (int ymcu = 0; ymcu < mcuRowsInScan; ymcu++) {
+		for (int xmcu = 0; xmcu < mcusPerRow; xmcu++) {
+			if (restartInterval != 0) {
+				if (restartsToGo == 0) processRestartInterval();
+				restartsToGo--;
+			}
+			decodeMCUAtXAndY(xmcu, ymcu, nComponentsInScan, first, start, end, approxBit);
+		}
+	}
+}
+int decodeUsingTable(JPEGHuffmanTable huffmanTable) {
+	int i = 0;
+	int[] maxCodes = huffmanTable.getDhMaxCodes();
+	int[] minCodes = huffmanTable.getDhMinCodes();
+	int[] valPtrs = huffmanTable.getDhValPtrs();
+	int[] huffVals = huffmanTable.getDhValues();
+	int code = nextBit();
+	while (code > maxCodes[i]) {
+		code = code * 2 + nextBit();
+		i++;
+	}
+	int j = valPtrs[i] + code - minCodes[i];
+	return huffVals[j];
+}
+void emit(int huffCode, int nBits) {
+	if (nBits == 0) {
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+	int[] power2m1 = [
+		1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
+		16383, 32767, 65535, 131125
+	];
+	int code = (huffCode & power2m1[nBits - 1]) << (24 - nBits - currentBitCount);
+	byte[] codeBuffer = new byte[4];
+	codeBuffer[0] = cast(byte)(code & 0xFF);
+	codeBuffer[1] = cast(byte)((code >> 8) & 0xFF);
+	codeBuffer[2] = cast(byte)((code >> 16) & 0xFF);
+	codeBuffer[3] = cast(byte)((code >> 24) & 0xFF);
+	int abs = nBits - (8 - currentBitCount);
+	if (abs < 0) abs = -abs;
+	if ((abs >> 3) > 0) {
+		currentByte += codeBuffer[2];
+		emitByte(cast(byte)currentByte);
+		emitByte(codeBuffer[1]);
+		currentByte = codeBuffer[0];
+		currentBitCount += nBits - 16;
+	} else {
+		currentBitCount += nBits;
+		if (currentBitCount >= 8) {
+			currentByte += codeBuffer[2];
+			emitByte(cast(byte)currentByte);
+			currentByte = codeBuffer[1];
+			currentBitCount -= 8;
+		} else {
+			currentByte += codeBuffer[2];
+		}
+	}
+}
+void emitByte(byte byteValue) {
+	if (bufferCurrentPosition >= 512) {
+		resetOutputBuffer();
+	}
+	dataBuffer[bufferCurrentPosition] = byteValue;
+	bufferCurrentPosition++;
+	if (byteValue == -1) {
+		emitByte(cast(byte)0);
+	}
+}
+void encodeACCoefficients(int[] dataUnit, int iComp) {
+	int[] sParams = scanHeader.componentParameters[iComp];
+	JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
+	int[] ehCodes = acTable.ehCodes;
+	byte[] ehSizes = acTable.ehCodeLengths;
+	int r = 0;
+	int k = 1;
+	while (k < 64) {
+		k++;
+		int acValue = dataUnit[ZigZag8x8[k - 1]];
+		if (acValue == 0) {
+			if (k == 64) {
+				emit(ehCodes[0], ehSizes[0] & 0xFF);
+			} else {
+				r++;
+			}
+		} else {
+			while (r > 15) {
+				emit(ehCodes[0xF0], ehSizes[0xF0] & 0xFF);
+				r -= 16;
+			}
+			if (acValue < 0) {
+				int absACValue = acValue;
+				if (absACValue < 0) absACValue = -absACValue;
+				int nBits = NBitsTable[absACValue];
+				int rs = r * 16 + nBits;
+				emit(ehCodes[rs], ehSizes[rs] & 0xFF);
+				emit(0xFFFFFF - absACValue, nBits);
+			} else {
+				int nBits = NBitsTable[acValue];
+				int rs = r * 16 + nBits;
+				emit(ehCodes[rs], ehSizes[rs] & 0xFF);
+				emit(acValue, nBits);
+			}
+			r = 0;
+		}
+	}
+}
+void encodeDCCoefficients(int[] dataUnit, int iComp) {
+	int[] sParams = scanHeader.componentParameters[iComp];
+	JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
+	int lastDC = precedingDCs[iComp];
+	int dcValue = dataUnit[0];
+	int diff = dcValue - lastDC;
+	precedingDCs[iComp] = dcValue;
+	if (diff < 0) {
+		int absDiff = 0 - diff;
+		int nBits = NBitsTable[absDiff];
+		emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
+		emit(0xFFFFFF - absDiff, nBits);
+	} else {
+		int nBits = NBitsTable[diff];
+		emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
+		if (nBits != 0) {
+			emit(diff, nBits);
+		}
+	}
+}
+void encodeMCUAtXAndY(int xmcu, int ymcu) {
+	int nComponentsInScan = scanHeader.getNumberOfImageComponents();
+	dataUnit = new int[64];
+	for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
+		int[] frameComponent = frameComponents[componentIds[iComp]];
+		int hi = frameComponent[HI];
+		int vi = frameComponent[VI];
+		for (int ivi = 0; ivi < vi; ivi++) {
+			for (int ihi = 0; ihi < hi; ihi++) {
+				extractData(dataUnit, iComp, xmcu, ymcu, ihi, ivi);
+				forwardDCT(dataUnit);
+				quantizeData(dataUnit, iComp);
+				encodeDCCoefficients(dataUnit, iComp);
+				encodeACCoefficients(dataUnit, iComp);
+			}
+		}
+	}
+}
+void encodeScan() {
+	for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
+		for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
+			encodeMCUAtXAndY(xmcu, ymcu);
+		}
+	}
+	if (currentBitCount != 0) {
+		emitByte(cast(byte)currentByte);
+	}
+	resetOutputBuffer();
+}
+void expandImageComponents() {
+	for (int iComp = 0; iComp < nComponents; iComp++) {
+		int[] frameComponent = frameComponents[componentIds[iComp]];
+		int hi = frameComponent[HI];
+		int vi = frameComponent[VI];
+		int upH = maxH / hi;
+		int upV = maxV / vi;
+		if ((upH * upV) > 1) {
+			byte[] component = imageComponents[iComp];
+			int compWidth = frameComponent[CW];
+			int compHeight = frameComponent[CH];
+			int upCompWidth = compWidth * upH;
+			int upCompHeight = compHeight * upV;
+			ImageData src = new ImageData(compWidth, compHeight, 8, new PaletteData(RGB16), 4, component);
+			ImageData dest = src.scaledTo(upCompWidth, upCompHeight);
+			imageComponents[iComp] = dest.data;
+		}
+	}
+}
+int extendBy(int diff, int t) {
+	if (diff < ExtendTest[t]) {
+		return diff + ExtendOffset[t];
+	} else {
+		return diff;
+	}
+}
+void extractData(int[] dataUnit, int iComp, int xmcu, int ymcu, int ihi, int ivi) {
+	byte[] compImage = imageComponents[iComp];
+	int[] frameComponent = frameComponents[componentIds[iComp]];
+	int hi = frameComponent[HI];
+	int vi = frameComponent[VI];
+	int compWidth = frameComponent[CW];
+	int srcIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
+	int destIndex = 0;
+	for (int i = 0; i < DCTSIZE; i++) {
+		for (int col = 0; col < DCTSIZE; col++) {
+			dataUnit[destIndex] = (compImage[srcIndex + col] & 0xFF) - 128;
+			destIndex++;
+		}
+		srcIndex += compWidth;
+	}
+}
+void forwardDCT(int[] dataUnit) {
+	for (int row = 0; row < 8; row++) {
+		int rIndex = row * DCTSIZE;
+		int tmp0 = dataUnit[rIndex] + dataUnit[rIndex + 7];
+		int tmp7 = dataUnit[rIndex] - dataUnit[rIndex + 7];
+		int tmp1 = dataUnit[rIndex + 1] + dataUnit[rIndex + 6];
+		int tmp6 = dataUnit[rIndex + 1] - dataUnit[rIndex + 6];
+		int tmp2 = dataUnit[rIndex + 2] + dataUnit[rIndex + 5];
+		int tmp5 = dataUnit[rIndex + 2] - dataUnit[rIndex + 5];
+		int tmp3 = dataUnit[rIndex + 3] + dataUnit[rIndex + 4];
+		int tmp4 = dataUnit[rIndex + 3] - dataUnit[rIndex + 4];
+
+		/**
+		 * Even part per LL&M figure 1 --- note that published figure
+		 * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
+		 */
+		int tmp10 = tmp0 + tmp3;
+		int tmp13 = tmp0 - tmp3;
+		int tmp11 = tmp1 + tmp2;
+		int tmp12 = tmp1 - tmp2;
+
+		dataUnit[rIndex] = (tmp10 + tmp11) * 4;
+		dataUnit[rIndex + 4]  = (tmp10 - tmp11) * 4;
+
+		int z1 = (tmp12 + tmp13) * FIX_0_541196100;
+		int n = z1 + (tmp13 * FIX_0_765366865) + 1024;
+		dataUnit[rIndex + 2] = n >> 11;
+		if ((n < 0) && ((n & 0x07FF) != 0)) dataUnit[rIndex + 2]--;
+		n = z1 + (tmp12 * (0 - FIX_1_847759065)) + 1024;
+ 		dataUnit[rIndex + 6] = n >> 11;
+		if ((n < 0) && ((n & 0x07FF) != 0)) dataUnit[rIndex + 6]--;
+
+		/**
+		 * Odd part per figure 8 --- note paper omits factor of sqrt(2).
+		 * cK represents cos(K*pi/16).
+		 * i0..i3 in the paper are tmp4..tmp7 here.
+		 */
+		z1 = tmp4 + tmp7;
+		int z2 = tmp5 + tmp6;
+		int z3 = tmp4 + tmp6;
+		int z4 = tmp5 + tmp7;
+		int z5 = (z3 + z4) * FIX_1_175875602;	// sqrt(2) * c3
+
+		tmp4 *= FIX_0_298631336;	// sqrt(2) * (-c1+c3+c5-c7)
+		tmp5 *= FIX_2_053119869;	// sqrt(2) * ( c1+c3-c5+c7)
+		tmp6 *= FIX_3_072711026;	// sqrt(2) * ( c1+c3+c5-c7)
+		tmp7 *= FIX_1_501321110;	// sqrt(2) * ( c1+c3-c5-c7)
+		z1 *= 0 - FIX_0_899976223;	// sqrt(2) * (c7-c3)
+		z2 *= 0 - FIX_2_562915447;	// sqrt(2) * (-c1-c3)
+		z3 *= 0 - FIX_1_961570560;	// sqrt(2) * (-c3-c5)
+		z4 *= 0 - FIX_0_390180644;	// sqrt(2) * (c5-c3)
+
+		z3 += z5;
+		z4 += z5;
+
+		n = tmp4 + z1 + z3 + 1024;
+		dataUnit[rIndex + 7] = n >> 11;
+		if ((n < 0) && ((n & 0x07FF) != 0)) dataUnit[rIndex + 7]--;
+		n = tmp5 + z2 + z4 + 1024;
+		dataUnit[rIndex + 5] = n >> 11;
+		if ((n < 0) && ((n & 0x07FF) != 0)) dataUnit[rIndex + 5]--;
+		n = tmp6 + z2 + z3 + 1024;
+		dataUnit[rIndex + 3] = n >> 11;
+		if ((n < 0) && ((n & 0x07FF) != 0)) dataUnit[rIndex + 3]--;
+		n = tmp7 + z1 + z4 + 1024;
+		dataUnit[rIndex + 1] = n >> 11;
+		if ((n < 0) && ((n & 0x07FF) != 0)) dataUnit[rIndex + 1]--;
+	}
+
+	/**
+	 * Pass 2: process columns.
+	 * Note that we must descale the results by a factor of 8 == 2**3,
+	 * and also undo the PASS1_BITS scaling.
+	 */
+	for (int col = 0; col < 8; col++) {
+		int c0 = col;
+		int c1 = col + 8;
+		int c2 = col + 16;
+		int c3 = col + 24;
+		int c4 = col + 32;
+		int c5 = col + 40;
+		int c6 = col + 48;
+		int c7 = col + 56;
+		int tmp0 = dataUnit[c0] + dataUnit[c7];
+		int tmp7 = dataUnit[c0] - dataUnit[c7];
+		int tmp1 = dataUnit[c1] + dataUnit[c6];
+		int tmp6 = dataUnit[c1] - dataUnit[c6];
+		int tmp2 = dataUnit[c2] + dataUnit[c5];
+		int tmp5 = dataUnit[c2] - dataUnit[c5];
+		int tmp3 = dataUnit[c3] + dataUnit[c4];
+		int tmp4 = dataUnit[c3] - dataUnit[c4];
+
+		/**
+		 * Even part per LL&M figure 1 --- note that published figure
+		 * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
+		 */
+		int tmp10 = tmp0 + tmp3;
+		int tmp13 = tmp0 - tmp3;
+		int tmp11 = tmp1 + tmp2;
+		int tmp12 = tmp1 - tmp2;
+
+		int n = tmp10 + tmp11 + 16;
+		dataUnit[c0] = n >> 5;
+		if ((n < 0) && ((n & 0x1F) != 0)) dataUnit[c0]--;
+		n = tmp10 - tmp11 + 16;
+		dataUnit[c4] = n >> 5;
+		if ((n < 0) && ((n & 0x1F) != 0)) dataUnit[c4]--;
+
+		int z1 = (tmp12 + tmp13) * FIX_0_541196100;
+		n = z1 + (tmp13 * FIX_0_765366865) + 131072;
+		dataUnit[c2] = n >> 18;
+		if ((n < 0) && ((n & 0x3FFFF) != 0)) dataUnit[c2]--;
+		n = z1 + (tmp12 * (0 - FIX_1_847759065)) + 131072;
+		dataUnit[c6] = n >> 18;
+		if ((n < 0) && ((n & 0x3FFFF) != 0)) dataUnit[c6]--;
+
+		/**
+		 * Odd part per figure 8 --- note paper omits factor of sqrt(2).
+		 * cK represents cos(K*pi/16).
+		 * i0..i3 in the paper are tmp4..tmp7 here.
+		 */
+		z1 = tmp4 + tmp7;
+		int z2 = tmp5 + tmp6;
+		int z3 = tmp4 + tmp6;
+		int z4 = tmp5 + tmp7;
+		int z5 = (z3 + z4) * FIX_1_175875602;	// sqrt(2) * c3
+
+		tmp4 *= FIX_0_298631336;	// sqrt(2) * (-c1+c3+c5-c7)
+		tmp5 *= FIX_2_053119869;	// sqrt(2) * ( c1+c3-c5+c7)
+		tmp6 *= FIX_3_072711026;	// sqrt(2) * ( c1+c3+c5-c7)
+		tmp7 *= FIX_1_501321110;	// sqrt(2) * ( c1+c3-c5-c7)
+		z1 *= 0 - FIX_0_899976223;	// sqrt(2) * (c7-c3)
+		z2 *= 0 - FIX_2_562915447;	// sqrt(2) * (-c1-c3)
+		z3 *= 0 - FIX_1_961570560;	// sqrt(2) * (-c3-c5)
+		z4 *= 0 - FIX_0_390180644;	// sqrt(2) * (c5-c3)
+
+		z3 += z5;
+		z4 += z5;
+
+		n = tmp4 + z1 + z3 + 131072;
+		dataUnit[c7] = n >> 18;
+		if ((n < 0) && ((n & 0x3FFFF) != 0)) dataUnit[c7]--;
+		n = tmp5 + z2 + z4 + 131072;
+		dataUnit[c5] = n >> 18;
+		if ((n < 0) && ((n & 0x3FFFF) != 0)) dataUnit[c5]--;
+		n = tmp6 + z2 + z3 + 131072;
+		dataUnit[c3] = n >> 18;
+		if ((n < 0) && ((n & 0x3FFFF) != 0)) dataUnit[c3]--;
+		n = tmp7 + z1 + z4 + 131072;
+		dataUnit[c1] = n >> 18;
+		if ((n < 0) && ((n & 0x3FFFF) != 0)) dataUnit[c1]--;
+	}
+}
+void getAPP0() {
+	JPEGAppn appn = new JPEGAppn(inputStream);
+	if (!appn.verify()) {
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+}
+void getCOM() {
+	new JPEGComment(inputStream);
+}
+void getDAC() {
+	new JPEGArithmeticConditioningTable(inputStream);
+}
+void getDHT() {
+	JPEGHuffmanTable dht = new JPEGHuffmanTable(inputStream);
+	if (!dht.verify()) {
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+	if (acHuffmanTables == null) {
+		acHuffmanTables = new JPEGHuffmanTable[4];
+	}
+	if (dcHuffmanTables == null) {
+		dcHuffmanTables = new JPEGHuffmanTable[4];
+	}
+	JPEGHuffmanTable[] dhtTables = dht.getAllTables();
+	for (int i = 0; i < dhtTables.length; i++) {
+		JPEGHuffmanTable dhtTable = dhtTables[i];
+		if (dhtTable.getTableClass() == 0) {
+			dcHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
+		} else {
+			acHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
+		}
+	}
+}
+void getDNL() {
+	new JPEGRestartInterval(inputStream);
+}
+void getDQT() {
+	JPEGQuantizationTable dqt = new JPEGQuantizationTable(inputStream);
+	int[][] currentTables = quantizationTables;
+	if (currentTables == null) {
+		currentTables = new int[][](4);
+	}
+	int[] dqtTablesKeys = dqt.getQuantizationTablesKeys();
+	int[][] dqtTablesValues = dqt.getQuantizationTablesValues();
+	for (int i = 0; i < dqtTablesKeys.length; i++) {
+		int index = dqtTablesKeys[i];
+		currentTables[index] = dqtTablesValues[i];
+	}
+	quantizationTables = currentTables;
+}
+void getDRI() {
+	JPEGRestartInterval dri = new JPEGRestartInterval(inputStream);
+	if (!dri.verify()) {
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+	restartInterval = dri.getRestartInterval();
+}
+static void initialize() {
+	initializeRGBYCbCrTables();
+	initializeYCbCrRGBTables();
+	initializeBitCountTable();
+}
+static void initializeBitCountTable() {
+	int nBits = 1;
+	int power2 = 2;
+	NBitsTable = new int[2048];
+	NBitsTable[0] = 0;
+	for (int i = 1; i < NBitsTable.length; i++) {
+		if (!(i < power2)) {
+			nBits++;
+			power2 *= 2;
+		}
+		NBitsTable[i] = nBits;
+	}
+}
+static void initializeRGBYCbCrTables() {
+	RYTable = new int[256];
+	GYTable = new int[256];
+	BYTable = new int[256];
+	RCbTable = new int[256];
+	GCbTable = new int[256];
+	BCbTable = new int[256];
+	RCrTable = BCbTable;
+	GCrTable = new int[256];
+	BCrTable = new int[256];
+	for (int i = 0; i < 256; i++) {
+		RYTable[i] = i * 19595;
+		GYTable[i] = i * 38470;
+		BYTable[i] = i * 7471 + 32768;
+		RCbTable[i] = i * -11059;
+		GCbTable[i] = i * -21709;
+		BCbTable[i] = i * 32768 + 8388608;
+		GCrTable[i] = i * -27439;
+		BCrTable[i] = i * -5329;
+	}
+}
+static void initializeYCbCrRGBTables() {
+	CrRTable = new int[256];
+	CbBTable = new int[256];
+	CrGTable = new int[256];
+	CbGTable = new int[256];
+	for (int i = 0; i < 256; i++) {
+		int x2 = 2 * i - 255;
+		CrRTable[i] = (45941 * x2 + 32768) >> 16;
+		CbBTable[i] = (58065 * x2 + 32768) >> 16;
+		CrGTable[i] = -23401 * x2;
+		CbGTable[i] = -11277 * x2 + 32768;
+	}
+}
+void inverseDCT(int[] dataUnit) {
+	for (int row = 0; row < 8; row++) {
+		int rIndex = row * DCTSIZE;
+		/**
+		 * Due to quantization, we will usually find that many of the input
+		 * coefficients are zero, especially the AC terms.  We can exploit this
+		 * by short-circuiting the IDCT calculation for any row in which all
+		 * the AC terms are zero.  In that case each output is equal to the
+		 * DC coefficient (with scale factor as needed).
+		 * With typical images and quantization tables, half or more of the
+		 * row DCT calculations can be simplified this way.
+		 */
+		if (isZeroInRow(dataUnit, rIndex)) {
+			int dcVal = dataUnit[rIndex] << 2;
+			for (int i = rIndex + 7; i >= rIndex; i--) {
+				dataUnit[i] = dcVal;
+			}
+		} else {
+			/**
+			 * Even part: reverse the even part of the forward DCT.
+			 * The rotator is sqrt(2)*c(-6).
+			 */
+			int z2 = dataUnit[rIndex + 2];
+			int z3 = dataUnit[rIndex + 6];
+			int z1 = (z2 + z3) * FIX_0_541196100;
+			int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
+			int tmp3 = z1 + (z2 * FIX_0_765366865);
+			int tmp0 = (dataUnit[rIndex] + dataUnit[rIndex + 4]) << 13;
+			int tmp1 = (dataUnit[rIndex] - dataUnit[rIndex + 4]) << 13;
+			int tmp10 = tmp0 + tmp3;
+			int tmp13 = tmp0 - tmp3;
+			int tmp11 = tmp1 + tmp2;
+			int tmp12 = tmp1 - tmp2;
+			/**
+			 * Odd part per figure 8; the matrix is unitary and hence its
+			 * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+			 */
+			tmp0 = dataUnit[rIndex + 7];
+			tmp1 = dataUnit[rIndex + 5];
+			tmp2 = dataUnit[rIndex + 3];
+			tmp3 = dataUnit[rIndex + 1];
+			z1 = tmp0 + tmp3;
+			z2 = tmp1 + tmp2;
+			z3 = tmp0 + tmp2;
+			int z4 = tmp1 + tmp3;
+			int z5 = (z3 + z4) * FIX_1_175875602; /* sqrt(2) * c3 */
+
+			tmp0 *= FIX_0_298631336;		/* sqrt(2) * (-c1+c3+c5-c7) */
+			tmp1 *= FIX_2_053119869;		/* sqrt(2) * ( c1+c3-c5+c7) */
+			tmp2 *= FIX_3_072711026;		/* sqrt(2) * ( c1+c3+c5-c7) */
+			tmp3 *= FIX_1_501321110;		/* sqrt(2) * ( c1+c3-c5-c7) */
+			z1 *= 0 - FIX_0_899976223;	/* sqrt(2) * (c7-c3) */
+			z2 *= 0 - FIX_2_562915447;	/* sqrt(2) * (-c1-c3) */
+			z3 *= 0 - FIX_1_961570560;	/* sqrt(2) * (-c3-c5) */
+			z4 *= 0 - FIX_0_390180644;	/* sqrt(2) * (c5-c3) */
+
+			z3 += z5;
+			z4 += z5;
+			tmp0 += z1 + z3;
+			tmp1 += z2 + z4;
+			tmp2 += z2 + z3;
+			tmp3 += z1 + z4;
+
+			dataUnit[rIndex] = (tmp10 + tmp3 + 1024) >> 11;
+			dataUnit[rIndex + 7] = (tmp10 - tmp3 + 1024) >> 11;
+			dataUnit[rIndex + 1] = (tmp11 + tmp2 + 1024) >> 11;
+			dataUnit[rIndex + 6] = (tmp11 - tmp2 + 1024) >> 11;
+			dataUnit[rIndex + 2] = (tmp12 + tmp1 + 1024) >> 11;
+			dataUnit[rIndex + 5] = (tmp12 - tmp1 + 1024) >> 11;
+			dataUnit[rIndex + 3] = (tmp13 + tmp0 + 1024) >> 11;
+			dataUnit[rIndex + 4] = (tmp13 - tmp0 + 1024) >> 11;
+		 }
+	}
+	/**
+	 * Pass 2: process columns.
+	 * Note that we must descale the results by a factor of 8 == 2**3,
+	 * and also undo the PASS1_BITS scaling.
+	 */
+	for (int col = 0; col < 8; col++) {
+		int c0 = col;
+		int c1 = col + 8;
+		int c2 = col + 16;
+		int c3 = col + 24;
+		int c4 = col + 32;
+		int c5 = col + 40;
+		int c6 = col + 48;
+		int c7 = col + 56;
+		if (isZeroInColumn(dataUnit, col)) {
+			int dcVal = (dataUnit[c0] + 16) >> 5;
+			dataUnit[c0] = dcVal;
+			dataUnit[c1] = dcVal;
+			dataUnit[c2] = dcVal;
+			dataUnit[c3] = dcVal;
+			dataUnit[c4] = dcVal;
+			dataUnit[c5] = dcVal;
+			dataUnit[c6] = dcVal;
+			dataUnit[c7] = dcVal;
+		} else {
+			/**
+			 * Even part: reverse the even part of the forward DCT.
+			 * The rotator is sqrt(2)*c(-6).
+			 */
+			int z0 = dataUnit[c0];
+			int z2 = dataUnit[c2];
+			int z3 = dataUnit[c6];
+			int z4 = dataUnit[c4];
+			int z1 = (z2 + z3) * FIX_0_541196100;
+			int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
+			int tmp3 = z1 + (z2 * FIX_0_765366865);
+			int tmp0 = (z0 + z4) << 13;
+			int tmp1 = (z0 - z4) << 13;
+			int tmp10 = tmp0 + tmp3;
+			int tmp13 = tmp0 - tmp3;
+			int tmp11 = tmp1 + tmp2;
+			int tmp12 = tmp1 - tmp2;
+			/**
+			 * Odd part per figure 8; the matrix is unitary and hence its
+			 * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
+			 */
+			tmp0 = dataUnit[c7];
+			tmp1 = dataUnit[c5];
+			tmp2 = dataUnit[c3];
+			tmp3 = dataUnit[c1];
+			z1 = tmp0 + tmp3;
+			z2 = tmp1 + tmp2;
+			z3 = tmp0 + tmp2;
+			z4 = tmp1 + tmp3;
+			z0 = (z3 + z4) * FIX_1_175875602;	/* sqrt(2) * c3 */
+
+			tmp0 *= FIX_0_298631336;		/* sqrt(2) * (-c1+c3+c5-c7) */
+			tmp1 *= FIX_2_053119869;		/* sqrt(2) * ( c1+c3-c5+c7) */
+			tmp2 *= FIX_3_072711026;		/* sqrt(2) * ( c1+c3+c5-c7) */
+			tmp3 *= FIX_1_501321110;		/* sqrt(2) * ( c1+c3-c5-c7) */
+			z1 *= 0 - FIX_0_899976223;	/* sqrt(2) * (c7-c3) */
+			z2 *= 0 - FIX_2_562915447;	/* sqrt(2) * (-c1-c3) */
+			z3 *= 0 - FIX_1_961570560;	/* sqrt(2) * (-c3-c5) */
+			z4 *= 0 - FIX_0_390180644;	/* sqrt(2) * (c5-c3) */
+
+			z3 += z0;
+			z4 += z0;
+
+			tmp0 += z1 + z3;
+			tmp1 += z2 + z4;
+			tmp2 += z2 + z3;
+			tmp3 += z1 + z4;
+
+			/* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+			dataUnit[c0] = (tmp10 + tmp3 + 131072) >> 18;
+			dataUnit[c7] = (tmp10 - tmp3 + 131072) >> 18;
+			dataUnit[c1] = (tmp11 + tmp2 + 131072) >> 18;
+			dataUnit[c6] = (tmp11 - tmp2 + 131072) >> 18;
+			dataUnit[c2] = (tmp12 + tmp1 + 131072) >> 18;
+			dataUnit[c5] = (tmp12 - tmp1 + 131072) >> 18;
+			dataUnit[c3] = (tmp13 + tmp0 + 131072) >> 18;
+			dataUnit[c4] = (tmp13 - tmp0 + 131072) >> 18;
+		}
+	}
+}
+bool isFileFormat(LEDataInputStream stream) {
+	try {
+		JPEGStartOfImage soi = new JPEGStartOfImage(stream);
+		stream.unread(soi.reference);
+		return soi.verify();  // we no longer check for appN
+	} catch (TracedException e) {
+		return false;
+	}
+}
+bool isZeroInColumn(int[] dataUnit, int col) {
+	return dataUnit[col + 8] == 0 && dataUnit[col + 16] == 0
+			&& dataUnit[col + 24] == 0 && dataUnit[col + 32] == 0
+			&& dataUnit[col + 40] == 0 && dataUnit[col + 48] == 0
+			&& dataUnit[col + 56] == 0;
+}
+bool isZeroInRow(int[] dataUnit, int rIndex) {
+	return dataUnit[rIndex + 1] == 0 && dataUnit[rIndex + 2] == 0
+			&& dataUnit[rIndex + 3] == 0 && dataUnit[rIndex + 4] == 0
+			&& dataUnit[rIndex + 5] == 0 && dataUnit[rIndex + 6] == 0
+			&& dataUnit[rIndex + 7] == 0;
+}
+ImageData[] loadFromByteStream() {
+	//TEMPORARY CODE
+    //PORTING_FIXME
+	if (/+System.getProperty("dwt.internal.image.JPEGFileFormat_3.2") == null+/ true ) {
+		return JPEGDecoder.loadFromByteStream(inputStream, loader);
+	}
+	JPEGStartOfImage soi = new JPEGStartOfImage(inputStream);
+	if (!soi.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+	restartInterval = 0;
+
+	/* Process the tables preceding the frame header. */
+	processTables();
+
+	/* Start of Frame. */
+	frameHeader = new JPEGFrameHeader(inputStream);
+	if (!frameHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+	imageWidth = frameHeader.getSamplesPerLine();
+	imageHeight = frameHeader.getNumberOfLines();
+	maxH = frameHeader.getMaxHFactor();
+	maxV = frameHeader.getMaxVFactor();
+	int mcuWidth = maxH * DCTSIZE;
+	int mcuHeight = maxV * DCTSIZE;
+	interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
+	interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
+	progressive = frameHeader.isProgressive();
+	samplePrecision = frameHeader.getSamplePrecision();
+	nComponents = frameHeader.getNumberOfImageComponents();
+	frameComponents = frameHeader.componentParameters;
+	componentIds = frameHeader.componentIdentifiers;
+	imageComponents = new byte[][](nComponents);
+	if (progressive) {
+		// Progressive jpeg: need to keep all of the data units.
+		dataUnits = new int[][][](nComponents);
+	} else {
+		// Sequential jpeg: only need one data unit.
+		dataUnit = new int[8 * 8];
+	}
+	for (int i = 0; i < nComponents; i++) {
+		int[] frameComponent = frameComponents[componentIds[i]];
+		int bufferSize = frameComponent[CW] * frameComponent[CH];
+		imageComponents[i] = new byte[bufferSize];
+		if (progressive) {
+			dataUnits[i] = new int[][](bufferSize);
+		}
+	}
+
+	/* Process the tables preceding the scan header. */
+	processTables();
+
+	/* Start of Scan. */
+	scanHeader = new JPEGScanHeader(inputStream);
+	if (!scanHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+
+	/* Process scan(s) and further tables until EOI. */
+	int progressiveScanCount = 0;
+	bool done = false;
+	while(!done) {
+		resetInputBuffer();
+		precedingDCs = new int[4];
+		decodeScan();
+		if (progressive && loader.hasListeners()) {
+			ImageData imageData = createImageData();
+			loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, false));
+			progressiveScanCount++;
+		}
+
+		/* Unread any buffered data before looking for tables again. */
+		int delta = 512 - bufferCurrentPosition - 1;
+		if (delta > 0) {
+			byte[] unreadBuffer = new byte[delta];
+            unreadBuffer[ 0 .. delta ] = dataBuffer[ bufferCurrentPosition+1 .. bufferCurrentPosition+1+delta ];
+			try {
+				inputStream.unread(unreadBuffer);
+			} catch (IOException e) {
+				SWT.error(SWT.ERROR_IO, e);
+			}
+		}
+
+		/* Process the tables preceding the next scan header. */
+		JPEGSegment jpegSegment = processTables();
+		if (jpegSegment == null || jpegSegment.getSegmentMarker() == EOI) {
+			done = true;
+		} else {
+			scanHeader = new JPEGScanHeader(inputStream);
+			if (!scanHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+	}
+
+	if (progressive) {
+		for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
+			for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
+				for (int iComp = 0; iComp < nComponents; iComp++) {
+					int[] frameComponent = frameComponents[componentIds[iComp]];
+					int hi = frameComponent[HI];
+					int vi = frameComponent[VI];
+					int compWidth = frameComponent[CW];
+					for (int ivi = 0; ivi < vi; ivi++) {
+						for (int ihi = 0; ihi < hi; ihi++) {
+							int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
+							dataUnit = dataUnits[iComp][index];
+							dequantize(dataUnit, iComp);
+							inverseDCT(dataUnit);
+							storeData(dataUnit, iComp, xmcu, ymcu, hi, ihi, vi, ivi);
+						}
+					}
+				}
+			}
+		}
+		dataUnits = null; // release memory
+	}
+	ImageData imageData = createImageData();
+	if (progressive && loader.hasListeners()) {
+		loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, true));
+	}
+	return [imageData];
+}
+ImageData createImageData() {
+	return ImageData.internal_new(
+		imageWidth,
+		imageHeight,
+		nComponents * samplePrecision,
+		setUpPalette(),
+		nComponents == 1 ? 4 : 1,
+		decodeImageComponents(),
+		0,
+		null,
+		null,
+		-1,
+		-1,
+		SWT.IMAGE_JPEG,
+		0,
+		0,
+		0,
+		0);
+}
+int nextBit() {
+	if (currentBitCount != 0) {
+		currentBitCount--;
+		currentByte *= 2;
+		if (currentByte > 255) {
+			currentByte -= 256;
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+	bufferCurrentPosition++;
+	if (bufferCurrentPosition >= 512) {
+		resetInputBuffer();
+		bufferCurrentPosition = 0;
+	}
+	currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+	currentBitCount = 8;
+	byte nextByte;
+	if (bufferCurrentPosition == 511) {
+		resetInputBuffer();
+		currentBitCount = 8;
+		nextByte = dataBuffer[0];
+	} else {
+		nextByte = dataBuffer[bufferCurrentPosition + 1];
+	}
+	if (currentByte == 0xFF) {
+		if (nextByte == 0) {
+			bufferCurrentPosition ++;
+			currentBitCount--;
+			currentByte *= 2;
+			if (currentByte > 255) {
+				currentByte -= 256;
+				return 1;
+			} else {
+				return 0;
+			}
+		} else {
+			if ((nextByte & 0xFF) + 0xFF00 == DNL) {
+				getDNL();
+				return 0;
+			} else {
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+				return 0;
+			}
+		}
+	} else {
+		currentBitCount--;
+		currentByte *= 2;
+		if (currentByte > 255) {
+			currentByte -= 256;
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+}
+void processRestartInterval() {
+	do {
+		bufferCurrentPosition++;
+		if (bufferCurrentPosition > 511) {
+			resetInputBuffer();
+			bufferCurrentPosition = 0;
+		}
+		currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+	} while (currentByte != 0xFF);
+	while (currentByte == 0xFF) {
+		bufferCurrentPosition++;
+		if (bufferCurrentPosition > 511) {
+			resetInputBuffer();
+			bufferCurrentPosition = 0;
+		}
+		currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+	}
+	if (currentByte != ((RST0 + nextRestartNumber) & 0xFF)) {
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+	bufferCurrentPosition++;
+	if (bufferCurrentPosition > 511) {
+		resetInputBuffer();
+		bufferCurrentPosition = 0;
+	}
+	currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
+	currentBitCount = 8;
+	restartsToGo = restartInterval;
+	nextRestartNumber = (nextRestartNumber + 1) & 0x7;
+	precedingDCs = new int[4];
+	eobrun = 0;
+}
+/* Process all markers until a frame header, scan header, or EOI is found. */
+JPEGSegment processTables() {
+	while (true) {
+		JPEGSegment jpegSegment = seekUnspecifiedMarker(inputStream);
+		if (jpegSegment == null) return null;
+		JPEGFrameHeader sof = new JPEGFrameHeader(jpegSegment.reference);
+		if (sof.verify()) {
+			return jpegSegment;
+		}
+		int marker = jpegSegment.getSegmentMarker();
+		switch (marker) {
+			case SOI: // there should only be one SOI per file
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+			case EOI:
+			case SOS:
+				return jpegSegment;
+			case DQT:
+				getDQT();
+				break;
+			case DHT:
+				getDHT();
+				break;
+			case DAC:
+				getDAC();
+				break;
+			case DRI:
+				getDRI();
+				break;
+			case APP0:
+				getAPP0();
+				break;
+			case COM:
+				getCOM();
+				break;
+			default:
+				skipSegmentFrom(inputStream);
+
+		}
+	}
+}
+void quantizeData(int[] dataUnit, int iComp) {
+	int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
+	for (int i = 0; i < dataUnit.length; i++) {
+		int zzIndex = ZigZag8x8[i];
+		int data = dataUnit[zzIndex];
+		int absData = data < 0 ? 0 - data : data;
+		int qValue = qTable[i];
+		int q2 = qValue >> 1;
+		absData += q2;
+		if (absData < qValue) {
+			dataUnit[zzIndex] = 0;
+		} else {
+			absData /= qValue;
+			if (data >= 0) {
+				dataUnit[zzIndex] = absData;
+			} else {
+				dataUnit[zzIndex] = 0 - absData;
+			}
+		}
+	}
+}
+int receive(int nBits) {
+	int v = 0;
+	for (int i = 0; i < nBits; i++) {
+		v = v * 2 + nextBit();
+	}
+	return v;
+}
+void resetInputBuffer() {
+	if (dataBuffer == null) {
+		dataBuffer = new byte[512];
+	}
+	try {
+		inputStream.read(dataBuffer);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	currentBitCount = 0;
+	bufferCurrentPosition = -1;
+}
+void resetOutputBuffer() {
+	if (dataBuffer == null) {
+		dataBuffer = new byte[512];
+	} else {
+		try {
+			outputStream.write(dataBuffer, 0, bufferCurrentPosition);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+	bufferCurrentPosition = 0;
+}
+static JPEGSegment seekUnspecifiedMarker(LEDataInputStream byteStream) {
+	byte[] byteArray = new byte[2];
+	try {
+		while (true) {
+			if (byteStream.read(byteArray, 0, 1) != 1) return null;
+			if (byteArray[0] == cast(byte) 0xFF) {
+				if (byteStream.read(byteArray, 1, 1) != 1) return null;
+				if (byteArray[1] != cast(byte) 0xFF && byteArray[1] != 0) {
+					byteStream.unread(byteArray);
+					return new JPEGSegment(byteArray);
+				}
+			}
+		}
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return null;
+}
+PaletteData setUpPalette() {
+	if (nComponents == 1) {
+		RGB[] entries = new RGB[256];
+		for (int i = 0; i < 256; i++) {
+			entries[i] = new RGB(i, i, i);
+		}
+		return new PaletteData(entries);
+	}
+	return new PaletteData(0xFF, 0xFF00, 0xFF0000);
+}
+static void skipSegmentFrom(LEDataInputStream byteStream) {
+	try {
+		byte[] byteArray = new byte[4];
+		JPEGSegment jpegSegment = new JPEGSegment(byteArray);
+
+		if (byteStream.read(byteArray) != byteArray.length) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		if (!(byteArray[0] == -1 && byteArray[1] != 0 && byteArray[1] != -1)) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		int delta = jpegSegment.getSegmentLength() - 2;
+		byteStream.skip(delta);
+	} catch (TracedException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+void storeData(int[] dataUnit, int iComp, int xmcu, int ymcu, int hi, int ihi, int vi, int ivi) {
+	byte[] compImage = imageComponents[iComp];
+	int[] frameComponent = frameComponents[componentIds[iComp]];
+	int compWidth = frameComponent[CW];
+	int destIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
+	int srcIndex = 0;
+	for (int i = 0; i < DCTSIZE; i++) {
+		for (int col = 0; col < DCTSIZE; col++) {
+			int x = dataUnit[srcIndex] + 128;
+			if (x < 0) {
+				x = 0;
+			} else {
+				if (x > 255) x = 255;
+			}
+			compImage[destIndex + col] = cast(byte)x;
+			srcIndex++;
+		}
+		destIndex += compWidth;
+	}
+}
+void unloadIntoByteStream(ImageLoader loader) {
+	ImageData image = loader.data[0];
+	if (!(new JPEGStartOfImage()).writeToStream(outputStream)) {
+		SWT.error(SWT.ERROR_IO);
+	}
+	JPEGAppn appn = new JPEGAppn([cast(byte)0xFF, cast(byte)0xE0, 0, 0x10, 0x4A, 0x46, 0x49, 0x46, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0]);
+	if (!appn.writeToStream(outputStream)) {
+		SWT.error(SWT.ERROR_IO);
+	}
+	quantizationTables = new int[][](4);
+	JPEGQuantizationTable chromDQT = JPEGQuantizationTable.defaultChrominanceTable();
+	chromDQT.scaleBy(encoderQFactor);
+	int[] jpegDQTKeys = chromDQT.getQuantizationTablesKeys();
+	int[][] jpegDQTValues = chromDQT.getQuantizationTablesValues();
+	for (int i = 0; i < jpegDQTKeys.length; i++) {
+		quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
+	}
+	JPEGQuantizationTable lumDQT = JPEGQuantizationTable.defaultLuminanceTable();
+	lumDQT.scaleBy(encoderQFactor);
+	jpegDQTKeys = lumDQT.getQuantizationTablesKeys();
+	jpegDQTValues = lumDQT.getQuantizationTablesValues();
+	for (int i = 0; i < jpegDQTKeys.length; i++) {
+		quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
+	}
+	if (!lumDQT.writeToStream(outputStream)) {
+		SWT.error(SWT.ERROR_IO);
+	}
+	if (!chromDQT.writeToStream(outputStream)) {
+		SWT.error(SWT.ERROR_IO);
+	}
+	int frameLength, scanLength, precision;
+	int[][] frameParams, scanParams;
+	if (image.depth == 1) {
+		frameLength = 11;
+		frameParams = new int[][](1);
+		frameParams[0] = [1, 1, 1, 0, 0];
+		scanParams = new int[][](1);
+		scanParams[0] = [0, 0];
+		scanLength = 8;
+		nComponents = 1;
+		precision = 1;
+	} else {
+		frameLength = 17;
+		frameParams = new int[][](3);
+		frameParams[0] = [0, 2, 2, 0, 0];
+		frameParams[1] = [1, 1, 1, 0, 0];
+		frameParams[2] = [1, 1, 1, 0, 0];
+		scanParams = new int[][](3);
+		scanParams[0] = [0, 0];
+		scanParams[1] = [1, 1];
+		scanParams[2] = [1, 1];
+		scanLength = 12;
+		nComponents = 3;
+		precision = 8;
+	}
+	imageWidth = image.width;
+	imageHeight = image.height;
+	frameHeader = new JPEGFrameHeader(new byte[19]);
+	frameHeader.setSegmentMarker(SOF0);
+	frameHeader.setSegmentLength(frameLength);
+	frameHeader.setSamplePrecision(precision);
+	frameHeader.setSamplesPerLine(imageWidth);
+	frameHeader.setNumberOfLines(imageHeight);
+	frameHeader.setNumberOfImageComponents(nComponents);
+	frameHeader.componentParameters = frameParams;
+	frameHeader.componentIdentifiers = [0, 1, 2];
+	frameHeader.initializeContents();
+	if (!frameHeader.writeToStream(outputStream)) {
+		SWT.error(SWT.ERROR_IO);
+	}
+	frameComponents = frameParams;
+	componentIds = frameHeader.componentIdentifiers;
+	maxH = frameHeader.getMaxHFactor();
+	maxV = frameHeader.getMaxVFactor();
+	int mcuWidth = maxH * DCTSIZE;
+	int mcuHeight = maxV * DCTSIZE;
+	interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
+	interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
+	acHuffmanTables = new JPEGHuffmanTable[4];
+	dcHuffmanTables = new JPEGHuffmanTable[4];
+	JPEGHuffmanTable[] dhtTables = [
+		JPEGHuffmanTable.getDefaultDCLuminanceTable(),
+		JPEGHuffmanTable.getDefaultDCChrominanceTable(),
+		JPEGHuffmanTable.getDefaultACLuminanceTable(),
+		JPEGHuffmanTable.getDefaultACChrominanceTable()
+	];
+	for (int i = 0; i < dhtTables.length; i++) {
+		JPEGHuffmanTable dhtTable = dhtTables[i];
+		if (!dhtTable.writeToStream(outputStream)) {
+			SWT.error(SWT.ERROR_IO);
+		}
+		JPEGHuffmanTable[] allTables = dhtTable.getAllTables();
+		for (int j = 0; j < allTables.length; j++) {
+			JPEGHuffmanTable huffmanTable = allTables[j];
+			if (huffmanTable.getTableClass() == 0) {
+				dcHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
+			} else {
+				acHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
+			}
+		}
+	}
+	precedingDCs = new int[4];
+	scanHeader = new JPEGScanHeader(new byte[14]);
+	scanHeader.setSegmentMarker(SOS);
+	scanHeader.setSegmentLength(scanLength);
+	scanHeader.setNumberOfImageComponents(nComponents);
+	scanHeader.setStartOfSpectralSelection(0);
+	scanHeader.setEndOfSpectralSelection(63);
+	scanHeader.componentParameters = scanParams;
+	scanHeader.initializeContents();
+	if (!scanHeader.writeToStream(outputStream)) {
+		SWT.error(SWT.ERROR_IO);
+	}
+	convertImageToYCbCr(image);
+	resetOutputBuffer();
+	currentByte = 0;
+	currentBitCount = 0;
+	encodeScan();
+	if (!(new JPEGEndOfImage()).writeToStream(outputStream)) {
+		SWT.error(SWT.ERROR_IO);
+	}
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGFixedSizeSegment.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGFixedSizeSegment;
+
+
+import dwt.SWT;
+import dwt.internal.image.JPEGSegment;
+import dwt.internal.image.LEDataInputStream;
+
+import tango.core.Exception;
+
+
+abstract class JPEGFixedSizeSegment : JPEGSegment {
+
+	public this() {
+		reference = new byte[fixedSize()];
+		setSegmentMarker(signature());
+	}
+
+	public this(byte[] reference) {
+		super(reference);
+	}
+
+	public this(LEDataInputStream byteStream) {
+		reference = new byte[fixedSize()];
+		try {
+			byteStream.read(reference);
+		} catch (TracedException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+
+	abstract public int fixedSize();
+
+	public int getSegmentLength() {
+		return fixedSize() - 2;
+	}
+
+	public void setSegmentLength(int length) {
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGFrameHeader.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.JPEGFrameHeader;
+
+
+import dwt.SWT;
+import dwt.internal.image.JPEGVariableSizeSegment;
+import dwt.internal.image.JPEGFileFormat;
+import dwt.internal.image.LEDataInputStream;
+
+final class JPEGFrameHeader : JPEGVariableSizeSegment {
+	int maxVFactor;
+	int maxHFactor;
+	public int[] componentIdentifiers;
+	public int[][] componentParameters;
+
+	public this(byte[] reference) {
+		super(reference);
+	}
+
+	public this(LEDataInputStream byteStream) {
+		super(byteStream);
+		initializeComponentParameters();
+	}
+
+	public int getSamplePrecision() {
+		return reference[4] & 0xFF;
+	}
+
+	public int getNumberOfLines() {
+		return (reference[5] & 0xFF) << 8 | (reference[6] & 0xFF);
+	}
+
+	public int getSamplesPerLine() {
+		return (reference[7] & 0xFF) << 8 | (reference[8] & 0xFF);
+	}
+
+	public int getNumberOfImageComponents() {
+		return reference[9] & 0xFF;
+	}
+
+	public void setSamplePrecision(int precision) {
+		reference[4] = cast(byte)(precision & 0xFF);
+	}
+
+	public void setNumberOfLines(int anInteger) {
+		reference[5] = cast(byte)((anInteger & 0xFF00) >> 8);
+		reference[6] = cast(byte)(anInteger & 0xFF);
+	}
+
+	public void setSamplesPerLine(int samples) {
+		reference[7] = cast(byte)((samples & 0xFF00) >> 8);
+		reference[8] = cast(byte)(samples & 0xFF);
+	}
+
+	public void setNumberOfImageComponents(int anInteger) {
+		reference[9] = cast(byte)(anInteger & 0xFF);
+	}
+
+	public int getMaxHFactor() {
+		return maxHFactor;
+	}
+
+	public int getMaxVFactor() {
+		return maxVFactor;
+	}
+
+	public void setMaxHFactor(int anInteger) {
+		maxHFactor = anInteger;
+	}
+
+	public void setMaxVFactor(int anInteger) {
+		maxVFactor = anInteger;
+	}
+
+	/* Used when decoding. */
+	void initializeComponentParameters() {
+		int nf = getNumberOfImageComponents();
+		componentIdentifiers = new int[nf];
+		int[][] compSpecParams;
+		int hmax = 1;
+		int vmax = 1;
+		for (int i = 0; i < nf; i++) {
+			int ofs = i * 3 + 10;
+			int ci = reference[ofs] & 0xFF;
+			componentIdentifiers[i] = ci;
+			int hi = (reference[ofs + 1] & 0xFF) >> 4;
+			int vi = reference[ofs + 1] & 0xF;
+			int tqi = reference[ofs + 2] & 0xFF;
+			if (hi > hmax) {
+				hmax = hi;
+			}
+			if (vi > vmax) {
+				vmax = vi;
+			}
+			int[] compParam = new int[5];
+			compParam[0] = tqi;
+			compParam[1] = hi;
+			compParam[2] = vi;
+			if (compSpecParams.length <= ci) {
+				compSpecParams.length = ci + 1;
+			}
+			compSpecParams[ci] = compParam;
+		}
+		int x = getSamplesPerLine();
+		int y = getNumberOfLines();
+		int[] multiples = [ 8, 16, 24, 32 ];
+		for (int i = 0; i < nf; i++) {
+			int[] compParam = compSpecParams[componentIdentifiers[i]];
+			int hi = compParam[1];
+			int vi = compParam[2];
+			int compWidth = (x * hi + hmax - 1) / hmax;
+			int compHeight = (y * vi + vmax - 1) / vmax;
+			int dsWidth = roundUpToMultiple(compWidth, multiples[hi - 1]);
+			int dsHeight = roundUpToMultiple(compHeight, multiples[vi - 1]);
+			compParam[3] = dsWidth;
+			compParam[4] = dsHeight;
+		}
+		setMaxHFactor(hmax);
+		setMaxVFactor(vmax);
+		componentParameters = compSpecParams;
+	}
+
+	/* Used when encoding. */
+	public void initializeContents() {
+		int nf = getNumberOfImageComponents();
+		if (nf == 0 || nf != componentParameters.length) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		int hmax = 0;
+		int vmax = 0;
+		int[][] compSpecParams = componentParameters;
+		for (int i = 0; i < nf; i++) {
+			int ofs = i * 3 + 10;
+			int[] compParam = compSpecParams[componentIdentifiers[i]];
+			int hi = compParam[1];
+			int vi = compParam[2];
+			if (hi * vi > 4) {
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+			}
+			reference[ofs] = cast(byte)(i + 1);
+			reference[ofs + 1] = cast(byte)(hi * 16 + vi);
+			reference[ofs + 2] = cast(byte)(compParam[0]);
+			if (hi > hmax) hmax = hi;
+			if (vi > vmax) vmax = vi;
+		}
+		int x = getSamplesPerLine();
+		int y = getNumberOfLines();
+		int[] multiples = [8, 16, 24, 32];
+		for (int i = 0; i < nf; i++) {
+			int[] compParam = compSpecParams[componentIdentifiers[i]];
+			int hi = compParam[1];
+			int vi = compParam[2];
+			int compWidth = (x * hi + hmax - 1) / hmax;
+			int compHeight = (y * vi + vmax - 1) / vmax;
+			int dsWidth = roundUpToMultiple(compWidth, multiples[hi - 1]);
+			int dsHeight = roundUpToMultiple(compHeight, multiples[vi - 1]);
+			compParam[3] = dsWidth;
+			compParam[4] = dsHeight;
+		}
+		setMaxHFactor(hmax);
+		setMaxVFactor(vmax);
+	}
+
+	int roundUpToMultiple(int anInteger, int mInteger) {
+		int a = anInteger + mInteger - 1;
+		return a - (a % mInteger);
+	}
+
+	/*
+	 * Verify the information contained in the receiver is correct.
+	 * Answer true if the header contains a valid marker. Otherwise,
+	 * answer false. Valid Start Of Frame markers are:
+	 *	SOF_0  - Baseline DCT, Huffman coding
+	 *	SOF_1  - Extended sequential DCT, Huffman coding
+	 *	SOF_2  - Progressive DCT, Huffman coding
+	 *	SOF_3  - Lossless (sequential), Huffman coding
+	 *	SOF_5  - Differential sequential, Huffman coding
+	 *	SOF_6  - Differential progressive, Huffman coding
+	 *	SOF_7  - Differential lossless, Huffman coding
+	 *	SOF_9  - Extended sequential DCT, arithmetic coding
+	 *	SOF_10 - Progressive DCT, arithmetic coding
+	 *	SOF_11 - Lossless (sequential), arithmetic coding
+	 *	SOF_13 - Differential sequential, arithmetic coding
+	 *	SOF_14 - Differential progressive, arithmetic coding
+	 *	SOF_15 - Differential lossless, arithmetic coding
+	 */
+	public bool verify() {
+		int marker = getSegmentMarker();
+		return (marker >= JPEGFileFormat.SOF0 && marker <= JPEGFileFormat.SOF3) ||
+			(marker >= JPEGFileFormat.SOF5 && marker <= JPEGFileFormat.SOF7) ||
+			(marker >= JPEGFileFormat.SOF9 && marker <= JPEGFileFormat.SOF11) ||
+			(marker >= JPEGFileFormat.SOF13 && marker <= JPEGFileFormat.SOF15);
+	}
+
+	public bool isProgressive() {
+		int marker = getSegmentMarker();
+		return marker == JPEGFileFormat.SOF2
+			|| marker == JPEGFileFormat.SOF6
+			|| marker == JPEGFileFormat.SOF10
+			|| marker == JPEGFileFormat.SOF14;
+	}
+
+	public bool isArithmeticCoding() {
+		return getSegmentMarker() >= JPEGFileFormat.SOF9;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGHuffmanTable.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,267 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.JPEGHuffmanTable;
+
+import dwt.internal.image.JPEGVariableSizeSegment;
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.JPEGFileFormat;
+
+/**
+ * JPEGHuffmanTable class actually represents two types of object:
+ * 1) A DHT (Define Huffman Tables) segment, which may represent
+ *	as many as 4 Huffman tables. In this case, the tables are
+ *	stored in the allTables array.
+ * 2) A single Huffman table. In this case, the allTables array
+ *	will be null.
+ * The 'reference' field is stored in both types of object, but
+ * 'initialize' is only called if the object represents a DHT.
+ */
+final class JPEGHuffmanTable : JPEGVariableSizeSegment {
+	JPEGHuffmanTable[] allTables;
+	int tableClass;
+	int tableIdentifier;
+	int[] dhMaxCodes;
+	int[] dhMinCodes;
+	int[] dhValPtrs;
+	int[] dhValues;
+	int[] ehCodes;
+	byte[] ehCodeLengths;
+	static byte[] DCLuminanceTable = [
+		cast(byte)255, cast(byte)196, 0, 31, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+	];
+	static byte[] DCChrominanceTable = [
+		cast(byte)255, cast(byte)196, 0, 31, 1, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+	];
+	static byte[] ACLuminanceTable = [
+		cast(byte)255, cast(byte)196, 0, cast(byte)181, 16, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125,
+		1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20,
+		50, cast(byte)129, cast(byte)145, cast(byte)161, 8, 35, 66, cast(byte)177, cast(byte)193, 21, 82, cast(byte)209, cast(byte)240, 36, 51, 98,
+		114, cast(byte)130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53,
+		54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87,
+		88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118,
+		119, 120, 121, 122, cast(byte)131, cast(byte)132, cast(byte)133, cast(byte)134, cast(byte)135, cast(byte)136, cast(byte)137, cast(byte)138, cast(byte)146, cast(byte)147, cast(byte)148,
+		cast(byte)149, cast(byte)150, cast(byte)151, cast(byte)152, cast(byte)153, cast(byte)154, cast(byte)162, cast(byte)163, cast(byte)164, cast(byte)165, cast(byte)166, cast(byte)167, cast(byte)168, cast(byte)169, cast(byte)170,
+		cast(byte)178, cast(byte)179, cast(byte)180, cast(byte)181, cast(byte)182, cast(byte)183, cast(byte)184, cast(byte)185, cast(byte)186, cast(byte)194, cast(byte)195, cast(byte)196, cast(byte)197, cast(byte)198, cast(byte)199,
+		cast(byte)200, cast(byte)201, cast(byte)202, cast(byte)210, cast(byte)211, cast(byte)212, cast(byte)213, cast(byte)214, cast(byte)215, cast(byte)216, cast(byte)217, cast(byte)218, cast(byte)225, cast(byte)226, cast(byte)227,
+		cast(byte)228, cast(byte)229, cast(byte)230, cast(byte)231, cast(byte)232, cast(byte)233, cast(byte)234, cast(byte)241, cast(byte)242, cast(byte)243, cast(byte)244, cast(byte)245, cast(byte)246, cast(byte)247, cast(byte)248,
+		cast(byte)249, cast(byte)250
+	];
+	static byte[] ACChrominanceTable = [
+		cast(byte)255, cast(byte)196, 0, cast(byte)181, 17, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0,
+		1, 2, 119, 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34,
+		50, cast(byte)129, 8, 20, 66, cast(byte)145, cast(byte)161, cast(byte)177, cast(byte)193, 9, 35,
+		51, 82, cast(byte)240, 21, 98, 114, cast(byte)209, 10, 22, 36, 52, cast(byte)225, 37,
+		cast(byte)241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67,
+		68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102,
+		103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, cast(byte)130,
+		cast(byte)131, cast(byte)132, cast(byte)133, cast(byte)134, cast(byte)135, cast(byte)136, cast(byte)137,
+		cast(byte)138, cast(byte)146, cast(byte)147, cast(byte)148, cast(byte)149, cast(byte)150, cast(byte)151,
+		cast(byte)152, cast(byte)153, cast(byte)154, cast(byte)162, cast(byte)163, cast(byte)164, cast(byte)165,
+		cast(byte)166, cast(byte)167, cast(byte)168, cast(byte)169, cast(byte)170, cast(byte)178, cast(byte)179,
+		cast(byte)180, cast(byte)181, cast(byte)182, cast(byte)183, cast(byte)184, cast(byte)185, cast(byte)186,
+		cast(byte)194, cast(byte)195, cast(byte)196, cast(byte)197, cast(byte)198, cast(byte)199, cast(byte)200,
+		cast(byte)201, cast(byte)202, cast(byte)210, cast(byte)211, cast(byte)212, cast(byte)213, cast(byte)214,
+		cast(byte)215, cast(byte)216, cast(byte)217, cast(byte)218, cast(byte)226, cast(byte)227, cast(byte)228,
+		cast(byte)229, cast(byte)230, cast(byte)231, cast(byte)232, cast(byte)233, cast(byte)234, cast(byte)242,
+		cast(byte)243, cast(byte)244, cast(byte)245, cast(byte)246, cast(byte)247, cast(byte)248, cast(byte)249,
+		cast(byte)250
+	];
+
+public this(byte[] reference) {
+	super(reference);
+}
+
+public this(LEDataInputStream byteStream) {
+	super(byteStream);
+	initialize();
+}
+
+public JPEGHuffmanTable[] getAllTables() {
+	return allTables;
+}
+
+public static JPEGHuffmanTable getDefaultACChrominanceTable() {
+	JPEGHuffmanTable result = new JPEGHuffmanTable(ACChrominanceTable);
+	result.initialize();
+	return result;
+}
+
+public static JPEGHuffmanTable getDefaultACLuminanceTable() {
+	JPEGHuffmanTable result = new JPEGHuffmanTable(ACLuminanceTable);
+	result.initialize();
+	return result;
+}
+
+public static JPEGHuffmanTable getDefaultDCChrominanceTable() {
+	JPEGHuffmanTable result = new JPEGHuffmanTable(DCChrominanceTable);
+	result.initialize();
+	return result;
+}
+
+public static JPEGHuffmanTable getDefaultDCLuminanceTable() {
+	JPEGHuffmanTable result = new JPEGHuffmanTable(DCLuminanceTable);
+	result.initialize();
+	return result;
+}
+
+public int[] getDhMaxCodes() {
+	return dhMaxCodes;
+}
+
+public int[] getDhMinCodes() {
+	return dhMinCodes;
+}
+
+public int[] getDhValPtrs() {
+	return dhValPtrs;
+}
+
+public int[] getDhValues() {
+	return dhValues;
+}
+
+public int getTableClass() {
+	return tableClass;
+}
+
+public int getTableIdentifier() {
+	return tableIdentifier;
+}
+
+void initialize() {
+	int totalLength = getSegmentLength() - 2;
+	int ofs = 4;
+	int[] bits = new int[16];
+	JPEGHuffmanTable[] huffTables = new JPEGHuffmanTable[8]; // maximum is 4 AC + 4 DC
+	int huffTableCount = 0;
+	while (totalLength > 0) {
+		int tc = (reference[ofs] & 0xFF) >> 4; // table class: AC (1) or DC (0)
+		int tid = reference[ofs] & 0xF; // table id: 0-1 baseline, 0-3 prog/ext
+		ofs++;
+
+		/* Read the 16 count bytes and add them together to get the table size. */
+		int count = 0;
+		for (int i = 0; i < bits.length; i++) {
+			int bCount = reference[ofs + i] & 0xFF;
+			bits[i] = bCount;
+			count += bCount;
+		}
+		ofs += 16;
+		totalLength -= 17;
+
+		/* Read the table. */
+		int[] huffVals = new int[count];
+		for (int i = 0; i < count; i++) {
+			huffVals[i] = reference[ofs + i] & 0xFF;
+		}
+		ofs += count;
+		totalLength -= count;
+
+		/* Calculate the lengths. */
+		int[] huffCodeLengths = new int[50]; // start with 50 and increment as needed
+		int huffCodeLengthsIndex = 0;
+		for (int i = 0; i < 16; i++) {
+			for (int j = 0; j < bits[i]; j++) {
+				if (huffCodeLengthsIndex >= huffCodeLengths.length) {
+					int[] newHuffCodeLengths = new int[huffCodeLengths.length + 50];
+                    newHuffCodeLengths[0 .. huffCodeLengths.length] = huffCodeLengths;
+					huffCodeLengths = newHuffCodeLengths;
+				}
+				huffCodeLengths[huffCodeLengthsIndex] = i + 1;
+				huffCodeLengthsIndex++;
+			}
+		}
+
+		/* Truncate huffCodeLengths to the correct size. */
+		if (huffCodeLengthsIndex < huffCodeLengths.length) {
+			int[] newHuffCodeLengths = new int[huffCodeLengthsIndex];
+            newHuffCodeLengths[0 .. huffCodeLengthsIndex] = huffCodeLengths[ 0 .. huffCodeLengthsIndex ];
+			huffCodeLengths = newHuffCodeLengths;
+		}
+
+		/* Calculate the Huffman codes. */
+		int[] huffCodes = new int[50]; // start with 50 and increment as needed
+		int huffCodesIndex = 0;
+		int k = 1;
+		int code = 0;
+		int si = huffCodeLengths[0];
+		int p = 0;
+		while (p < huffCodeLengthsIndex) {
+			while ((p < huffCodeLengthsIndex) && (huffCodeLengths[p] == si)) {
+				if (huffCodesIndex >= huffCodes.length) {
+					int[] newHuffCodes = new int[huffCodes.length + 50];
+                    newHuffCodes[ 0 .. huffCodes.length ] = huffCodes;
+					huffCodes = newHuffCodes;
+				}
+				huffCodes[huffCodesIndex] = code;
+				huffCodesIndex++;
+				code++;
+				p++;
+			}
+			code *= 2;
+			si++;
+		}
+
+		/* Truncate huffCodes to the correct size. */
+		if (huffCodesIndex < huffCodes.length) {
+			int[] newHuffCodes = new int[huffCodesIndex];
+            newHuffCodes[ 0 .. huffCodesIndex ] = huffCodes[ 0 .. huffCodesIndex ];
+			huffCodes = newHuffCodes;
+		}
+
+		/* Calculate the maximum and minimum codes */
+		k = 0;
+		int[] maxCodes = new int[16];
+		int[] minCodes = new int[16];
+		int[] valPtrs = new int[16];
+		for (int i = 0; i < 16; i++) {
+			int bSize = bits[i];
+			if (bSize == 0) {
+				maxCodes[i] = -1;
+			} else {
+				valPtrs[i] = k;
+				minCodes[i] = huffCodes[k];
+				k += bSize;
+				maxCodes[i] = huffCodes[k - 1];
+			}
+		}
+
+		/* Calculate the eHuffman codes and lengths. */
+		int[] eHuffCodes = new int[256];
+		byte[] eHuffSize = new byte[256];
+		for (int i = 0; i < huffCodesIndex; i++) {
+			eHuffCodes[huffVals[i]] = huffCodes[i];
+			eHuffSize[huffVals[i]] = cast(byte)huffCodeLengths[i];
+		}
+
+		/* Create the new JPEGHuffmanTable and add it to the allTables array. */
+		JPEGHuffmanTable dhtTable = new JPEGHuffmanTable(reference);
+		dhtTable.tableClass = tc;
+		dhtTable.tableIdentifier = tid;
+		dhtTable.dhValues = huffVals;
+		dhtTable.dhMinCodes = minCodes;
+		dhtTable.dhMaxCodes = maxCodes;
+		dhtTable.dhValPtrs = valPtrs;
+		dhtTable.ehCodes = eHuffCodes;
+		dhtTable.ehCodeLengths = eHuffSize;
+		huffTables[huffTableCount] = dhtTable;
+		huffTableCount++;
+	}
+	allTables = new JPEGHuffmanTable[huffTableCount];
+    allTables[ 0 .. huffTableCount ] = huffTables[ 0 .. huffTableCount ];
+}
+
+public int signature() {
+	return JPEGFileFormat.DHT;
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGQuantizationTable.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * 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.JPEGQuantizationTable;
+
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.JPEGVariableSizeSegment;
+import dwt.internal.image.JPEGFileFormat;
+
+final class JPEGQuantizationTable : JPEGVariableSizeSegment {
+	public static byte[] DefaultLuminanceQTable = [
+		cast(byte)255, cast(byte)219, 0, 67, 0,
+		16, 11, 10, 16, 24, 40, 51, 61,
+		12, 12, 14, 19, 26, 58, 60, 55,
+		14, 13, 16, 24, 40, 57, 69, 56,
+		14, 17, 22, 29, 51, 87, 80, 62,
+		18, 22, 37, 56, 68, 109, 103, 77,
+		24, 35, 55, 64, 81, 104, 113, 92,
+		49, 64, 78, 87, 103, 121, 120, 101,
+		72, 92, 95, 98, 112, 100, 103, 99
+	];
+	public static byte[] DefaultChrominanceQTable = [
+		cast(byte)255, cast(byte)219, 0, 67, 1,
+		17, 18, 24, 47, 99, 99, 99, 99,
+		18, 21, 26, 66, 99, 99, 99, 99,
+		24, 26, 56, 99, 99, 99, 99, 99,
+		47, 66, 99, 99, 99, 99, 99, 99,
+		99, 99, 99, 99, 99, 99, 99, 99,
+		99, 99, 99, 99, 99, 99, 99, 99,
+		99, 99, 99, 99, 99, 99, 99, 99,
+		99, 99, 99, 99, 99, 99, 99, 99
+	];
+
+public this(byte[] reference) {
+	super(reference);
+}
+
+public this(LEDataInputStream byteStream) {
+	super(byteStream);
+}
+
+public static JPEGQuantizationTable defaultChrominanceTable() {
+	return new JPEGQuantizationTable(DefaultChrominanceQTable.dup);
+}
+
+public static JPEGQuantizationTable defaultLuminanceTable() {
+	return new JPEGQuantizationTable(DefaultLuminanceQTable.dup);
+}
+
+public int[] getQuantizationTablesKeys() {
+	int[] keys = new int[4];
+	int keysIndex = 0;
+	int totalLength = getSegmentLength() - 2;
+	int ofs = 4;
+	while (totalLength > 64) {
+		int tq = reference[ofs] & 0xF;
+		int pq = (reference[ofs] & 0xFF) >> 4;
+		if (pq == 0) {
+			ofs += 65;
+			totalLength -= 65;
+		} else {
+			ofs += 129;
+			totalLength -= 129;
+		}
+		if (keysIndex >= keys.length) {
+			keys.length = keys.length + 4;
+		}
+		keys[keysIndex] = tq;
+		keysIndex++;
+	}
+	return keys[ 0 .. keysIndex ].dup;
+}
+
+public int[][] getQuantizationTablesValues() {
+	int[][] values = new int[][](4);
+	int valuesIndex = 0;
+	int totalLength = getSegmentLength() - 2;
+	int ofs = 4;
+	while (totalLength > 64) {
+		int[] qk = new int[64];
+		int pq = (reference[ofs] & 0xFF) >> 4;
+		if (pq == 0) {
+			for (int i = 0; i < qk.length; i++) {
+				qk[i] = reference[ofs + i + 1] & 0xFF;
+			}
+			ofs += 65;
+			totalLength -= 65;
+		} else {
+			for (int i = 0; i < qk.length; i++) {
+				int idx = (i - 1) * 2 ;
+				qk[i] = (reference[ofs + idx + 1] & 0xFF) * 256 + (reference[ofs + idx + 2] & 0xFF);
+			}
+			ofs += 129;
+			totalLength -= 129;
+		}
+		if (valuesIndex >= values.length) {
+            values.length = values.length + 4;
+		}
+		values[valuesIndex] = qk;
+		valuesIndex++;
+	}
+	return values[ 0 .. valuesIndex ].dup;
+}
+
+public void scaleBy(int qualityFactor) {
+	int qFactor = qualityFactor;
+	if (qFactor <= 0) {
+		qFactor = 1;
+	}
+	if (qFactor > 100) {
+		qFactor = 100;
+	}
+	if (qFactor < 50) {
+		qFactor = 5000 / qFactor;
+	} else {
+		qFactor = 200 - (qFactor * 2);
+	}
+	int totalLength = getSegmentLength() - 2;
+	int ofs = 4;
+	while (totalLength > 64) {
+//		int tq = reference[ofs] & 0xFF;
+		int pq = (reference[ofs] & 0xFF) >> 4;
+		if (pq == 0) {
+			for (int i = ofs + 1; i <= ofs + 64; i++) {
+				int temp = ((reference[i] & 0xFF) * qFactor + 50) / 100;
+				if (temp <= 0) temp = 1;
+				if (temp > 255) temp = 255;
+				reference[i] = cast(byte)temp;
+			}
+			ofs += 65;
+			totalLength -= 65;
+		} else {
+			for (int i = ofs + 1; i <= ofs + 128; i += 2) {
+				int temp = (((reference[i] & 0xFF) * 256 + (reference[i + 1] & 0xFF)) * qFactor + 50) / 100;
+				if (temp <= 0) temp = 1;
+				if (temp > 32767) temp = 32767;
+				reference[i] = cast(byte)(temp >> 8);
+				reference[i + 1] = cast(byte)(temp & 0xFF);
+			}
+			ofs += 129;
+			totalLength -= 129;
+		}
+	}
+}
+
+public int signature() {
+	return JPEGFileFormat.DQT;
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGRestartInterval.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGRestartInterval;
+
+import dwt.internal.image.JPEGFixedSizeSegment;
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.JPEGFileFormat;
+
+final class JPEGRestartInterval : JPEGFixedSizeSegment {
+
+	public this(LEDataInputStream byteStream) {
+		super(byteStream);
+	}
+
+	public int signature() {
+		return JPEGFileFormat.DRI;
+	}
+
+	public int getRestartInterval() {
+		return ((reference[4] & 0xFF) << 8 | (reference[5] & 0xFF));
+	}
+
+	public int fixedSize() {
+		return 6;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGScanHeader.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.JPEGScanHeader;
+
+import dwt.SWT;
+import dwt.internal.image.JPEGVariableSizeSegment;
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.JPEGFileFormat;
+
+final class JPEGScanHeader : JPEGVariableSizeSegment {
+	public int[][] componentParameters;
+
+public this(byte[] reference) {
+	super(reference);
+}
+
+public this(LEDataInputStream byteStream) {
+	super(byteStream);
+	initializeComponentParameters();
+}
+
+public int getApproxBitPositionHigh() {
+	return reference[(2 * getNumberOfImageComponents()) + 7] >> 4;
+}
+
+public int getApproxBitPositionLow() {
+	return reference[(2 * getNumberOfImageComponents()) + 7] & 0xF;
+}
+
+public int getEndOfSpectralSelection() {
+	return reference[(2 * getNumberOfImageComponents()) + 6];
+}
+
+public int getNumberOfImageComponents() {
+	return reference[4];
+}
+
+public int getStartOfSpectralSelection() {
+	return reference[(2 * getNumberOfImageComponents()) + 5];
+}
+
+/* Used when decoding. */
+void initializeComponentParameters() {
+	int compCount = getNumberOfImageComponents();
+	componentParameters = null;
+	for (int i = 0; i < compCount; i++) {
+		int ofs = 5 + i * 2;
+		int cid = reference[ofs] & 0xFF;
+		int dc = (reference[ofs + 1] & 0xFF) >> 4;
+		int ac = reference[ofs + 1] & 0xF;
+		if (componentParameters.length <= cid) {
+			int[][] newParams = new int[][](cid + 1);
+            newParams[0 .. componentParameters.length] = componentParameters;
+			componentParameters = newParams;
+		}
+		componentParameters[cid] = [ dc, ac ];
+	}
+}
+
+/* Used when encoding. */
+public void initializeContents() {
+	int compCount = getNumberOfImageComponents();
+	int[][] compSpecParams = componentParameters;
+	if (compCount == 0 || compCount != compSpecParams.length) {
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+	for (int i = 0; i < compCount; i++) {
+		int ofs = i * 2 + 5;
+		int[] compParams = compSpecParams[i];
+		reference[ofs] = cast(byte)(i + 1);
+		reference[ofs + 1] = cast(byte)(compParams[0] * 16 + compParams[1]);
+	}
+}
+
+public void setEndOfSpectralSelection(int anInteger) {
+	reference[(2 * getNumberOfImageComponents()) + 6] = cast(byte)anInteger;
+}
+
+public void setNumberOfImageComponents(int anInteger) {
+	reference[4] = cast(byte)(anInteger & 0xFF);
+}
+
+public void setStartOfSpectralSelection(int anInteger) {
+	reference[(2 * getNumberOfImageComponents()) + 5] = cast(byte)anInteger;
+}
+
+public int signature() {
+	return JPEGFileFormat.SOS;
+}
+
+public bool verifyProgressiveScan() {
+	int start = getStartOfSpectralSelection();
+	int end = getEndOfSpectralSelection();
+	int low = getApproxBitPositionLow();
+	int high = getApproxBitPositionHigh();
+	int count = getNumberOfImageComponents();
+	if ((start == 0 && end == 00) || (start <= end && end <= 63)) {
+		if (low <= 13 && high <= 13 && (high == 0 || high == low + 1)) {
+			return start == 0 || (start > 0 && count == 1);
+		}
+	}
+	return false;
+}
+
+public bool isACProgressiveScan() {
+	return getStartOfSpectralSelection() != 0 && getEndOfSpectralSelection() != 0;
+}
+
+public bool isDCProgressiveScan() {
+	return getStartOfSpectralSelection() == 0 && getEndOfSpectralSelection() == 0;
+}
+
+public bool isFirstScan() {
+	return getApproxBitPositionHigh() == 0;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGSegment.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGSegment;
+
+import dwt.internal.image.LEDataOutputStream;
+
+import tango.core.Exception;
+
+class JPEGSegment {
+	public byte[] reference;
+
+	this() {
+	}
+
+	public this(byte[] reference) {
+		this.reference = reference;
+	}
+
+	public int signature() {
+		return 0;
+	}
+
+	public bool verify() {
+		return getSegmentMarker() == signature();
+	}
+
+	public int getSegmentMarker() {
+		return ((reference[0] & 0xFF) << 8 | (reference[1] & 0xFF));
+	}
+
+	public void setSegmentMarker(int marker) {
+		reference[0] = cast(byte)((marker & 0xFF00) >> 8);
+		reference[1] = cast(byte)(marker & 0xFF);
+	}
+
+	public int getSegmentLength() {
+		return ((reference[2] & 0xFF) << 8 | (reference[3] & 0xFF));
+	}
+
+	public void setSegmentLength(int length) {
+		reference[2] = cast(byte)((length & 0xFF00) >> 8);
+		reference[3] = cast(byte)(length & 0xFF);
+	}
+
+	public bool writeToStream(LEDataOutputStream byteStream) {
+		try {
+			byteStream.write(reference);
+			return true;
+		} catch (TracedException e) {
+			return false;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGStartOfImage.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGStartOfImage;
+
+import dwt.internal.image.JPEGFixedSizeSegment;
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.JPEGFileFormat;
+
+final class JPEGStartOfImage : JPEGFixedSizeSegment {
+
+	public this() {
+		super();
+	}
+
+	public this(byte[] reference) {
+		super(reference);
+	}
+
+	public this(LEDataInputStream byteStream) {
+		super(byteStream);
+	}
+
+	public int signature() {
+		return JPEGFileFormat.SOI;
+	}
+
+	public int fixedSize() {
+		return 2;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/JPEGVariableSizeSegment.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.JPEGVariableSizeSegment;
+
+import dwt.SWT;
+import dwt.internal.image.JPEGSegment;
+import dwt.internal.image.LEDataInputStream;
+
+import tango.core.Exception;
+
+abstract class JPEGVariableSizeSegment : JPEGSegment {
+
+	public this(byte[] reference) {
+		super(reference);
+	}
+
+	public this(LEDataInputStream byteStream) {
+		try {
+			byte[] header = new byte[4];
+			byteStream.read(header);
+			reference = header; // to use getSegmentLength()
+			byte[] contents = new byte[getSegmentLength() + 2];
+			contents[0] = header[0];
+			contents[1] = header[1];
+			contents[2] = header[2];
+			contents[3] = header[3];
+			byteStream.read(contents, 4, contents.length - 4);
+			reference = contents;
+		} catch (TracedException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+}
--- a/dwt/internal/image/LEDataInputStream.d	Sun Jan 06 19:36:29 2008 +0100
+++ b/dwt/internal/image/LEDataInputStream.d	Sun Jan 06 22:54:14 2008 +0100
@@ -16,6 +16,8 @@
 
 final class LEDataInputStream : InputStream{
 
+    alias InputStream.read read;
+
     InputStream host;
 
 	int position;
--- a/dwt/internal/image/LEDataOutputStream.d	Sun Jan 06 19:36:29 2008 +0100
+++ b/dwt/internal/image/LEDataOutputStream.d	Sun Jan 06 22:54:14 2008 +0100
@@ -14,6 +14,7 @@
 import dwt.dwthelper.OutputStream;
 
 final class LEDataOutputStream : OutputStream {
+    alias OutputStream.write write;
 	OutputStream ostr;
 public this(OutputStream output) {
 	this.ostr = output;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/LZWCodec.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,485 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.LZWCodec;
+
+
+import dwt.internal.image.LZWNode;
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.LEDataOutputStream;
+import dwt.SWT;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoader;
+import dwt.graphics.ImageLoaderEvent;
+
+import tango.core.Exception;
+
+final class LZWCodec {
+	int bitsPerPixel, blockSize, blockIndex, currentByte, bitsLeft,
+		codeSize, clearCode, endCode, newCodes, topSlot, currentSlot,
+		imageWidth, imageHeight, imageX, imageY, pass, line, codeMask;
+	byte[] block, lineArray;
+	int[] stack, suffix, prefix;
+	LZWNode[] nodeStack;
+	LEDataInputStream inputStream;
+	LEDataOutputStream outputStream;
+	ImageData image;
+	ImageLoader loader;
+	bool interlaced;
+	static final int[] MASK_TABLE = [
+		0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F,
+		0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF
+	];
+
+/**
+ * Decode the input.
+ */
+void decode() {
+	int code;
+	int oc = 0;
+	int fc = 0;
+	byte[] buf = new byte[imageWidth];
+	int stackIndex = 0;
+	int bufIndex = 0;
+	int c;
+	while ((c = nextCode()) != endCode) {
+		if (c == clearCode) {
+			codeSize = bitsPerPixel + 1;
+			codeMask = MASK_TABLE[bitsPerPixel];
+			currentSlot = newCodes;
+			topSlot = 1 << codeSize;
+			while ((c = nextCode()) == clearCode) {}
+			if (c != endCode) {
+				oc = fc = c;
+				buf[bufIndex] = cast(byte)c;
+				bufIndex++;
+				if (bufIndex == imageWidth) {
+					nextPutPixels(buf);
+					bufIndex = 0;
+				}
+			}
+		} else {
+			code = c;
+			if (code >= currentSlot) {
+				code = oc;
+				stack[stackIndex] = fc;
+				stackIndex++;
+			}
+			while (code >= newCodes) {
+				stack[stackIndex] = suffix[code];
+				stackIndex++;
+				code = prefix[code];
+			}
+			stack[stackIndex] = code;
+			stackIndex++;
+			if (currentSlot < topSlot) {
+				fc = code;
+				suffix[currentSlot] = fc;
+				prefix[currentSlot] = oc;
+				currentSlot++;
+				oc = c;
+			}
+			if (currentSlot >= topSlot) {
+				if (codeSize < 12) {
+					codeMask = MASK_TABLE[codeSize];
+					codeSize++;
+					topSlot = topSlot + topSlot;
+				}
+			}
+			while (stackIndex > 0) {
+				stackIndex--;
+				buf[bufIndex] = cast(byte)stack[stackIndex];
+				bufIndex++;
+				if (bufIndex == imageWidth) {
+					nextPutPixels(buf);
+					bufIndex = 0;
+				}
+			}
+		}
+	}
+	if (bufIndex != 0 && line < imageHeight) {
+		nextPutPixels(buf);
+	}
+}
+/**
+ * Decode the LZW-encoded bytes in the given byte stream
+ * into the given DeviceIndependentImage.
+ */
+public void decode(LEDataInputStream inputStream, ImageLoader loader, ImageData image, bool interlaced, int depth) {
+	this.inputStream = inputStream;
+	this.loader = loader;
+	this.image = image;
+	this.interlaced = interlaced;
+	this.bitsPerPixel = depth;
+	initializeForDecoding();
+	decode();
+}
+/**
+ * Encode the image.
+ */
+void encode() {
+	nextPutCode(clearCode);
+	int lastPrefix = encodeLoop();
+	nextPutCode(lastPrefix);
+	nextPutCode(endCode);
+
+	// Write out last partial block
+	if (bitsLeft == 8) {
+		block[0] = cast(byte)(blockIndex - 1); // Nothing in last byte
+	} else {
+		block[0] = cast(byte)(blockIndex); // Last byte has data
+	}
+	writeBlock();
+
+	// Write out empty block to indicate the end (if needed)
+	if (block[0] != 0) {
+		block[0] = 0;
+		writeBlock();
+	}
+}
+/**
+ * Encode the bytes into the given byte stream
+ * from the given DeviceIndependentImage.
+ */
+public void encode(LEDataOutputStream byteStream, ImageData image) {
+	this.outputStream = byteStream;
+	this.image = image;
+	initializeForEncoding();
+	encode();
+}
+/**
+ * Encoding loop broken out to allow early return.
+ */
+int encodeLoop() {
+	int pixel = nextPixel();
+	bool found;
+	LZWNode node;
+	while (true) {
+		int currentPrefix = pixel;
+		node = nodeStack[currentPrefix];
+		found = true;
+		pixel = nextPixel();
+		if (pixel < 0)
+			return currentPrefix;
+		while (found && (node.children != null)) {
+			node = node.children;
+			while (found && (node.suffix != pixel)) {
+				if (pixel < node.suffix) {
+					if (node.left == null) {
+						node.left = new LZWNode();
+						found = false;
+					}
+					node = node.left;
+				} else {
+					if (node.right == null) {
+						node.right = new LZWNode();
+						found = false;
+					}
+					node = node.right;
+				}
+			}
+			if (found) {
+				currentPrefix = node.code;
+				pixel = nextPixel();
+				if (pixel < 0)
+					return currentPrefix;
+			}
+		}
+		if (found) {
+			node.children = new LZWNode();
+			node = node.children;
+		}
+		node.children = null;
+		node.left = null;
+		node.right = null;
+		node.code = currentSlot;
+		node.prefix = currentPrefix;
+		node.suffix = pixel;
+		nextPutCode(currentPrefix);
+		currentSlot++;
+		// Off by one?
+		if (currentSlot < 4096) {
+			if (currentSlot > topSlot) {
+				codeSize++;
+				codeMask = MASK_TABLE[codeSize - 1];
+				topSlot *= 2;
+			}
+		} else {
+			nextPutCode(clearCode);
+			for (int i = 0; i < nodeStack.length; i++)
+				nodeStack[i].children = null;
+			codeSize = bitsPerPixel + 1;
+			codeMask = MASK_TABLE[codeSize - 1];
+			currentSlot = newCodes;
+			topSlot = 1 << codeSize;
+		}
+	}
+}
+/**
+ * Initialize the receiver for decoding the given
+ * byte array.
+ */
+void initializeForDecoding() {
+	pass = 1;
+	line = 0;
+	codeSize = bitsPerPixel + 1;
+	topSlot = 1 << codeSize;
+	clearCode = 1 << bitsPerPixel;
+	endCode = clearCode + 1;
+	newCodes = currentSlot = endCode + 1;
+	currentByte = -1;
+	blockSize = bitsLeft = 0;
+	blockIndex = 0;
+	codeMask = MASK_TABLE[codeSize - 1];
+	stack = new int[4096];
+	suffix = new int[4096];
+	prefix = new int[4096];
+	block = new byte[256];
+	imageWidth = image.width;
+	imageHeight = image.height;
+}
+/**
+ * Initialize the receiver for encoding the given
+ * byte array.
+ */
+void initializeForEncoding() {
+	interlaced = false;
+	bitsPerPixel = image.depth;
+	codeSize = bitsPerPixel + 1;
+	topSlot = 1 << codeSize;
+	clearCode = 1 << bitsPerPixel;
+	endCode = clearCode + 1;
+	newCodes = currentSlot = endCode + 1;
+	bitsLeft = 8;
+	currentByte = 0;
+	blockIndex = 1;
+	blockSize = 255;
+	block = new byte[blockSize];
+	block[0] = cast(byte)(blockSize - 1);
+	nodeStack = new LZWNode[1 << bitsPerPixel];
+	for (int i = 0; i < nodeStack.length; i++) {
+		LZWNode node = new LZWNode();
+		node.code = i + 1;
+		node.prefix = -1;
+		node.suffix = i + 1;
+		nodeStack[i] = node;
+	}
+	imageWidth = image.width;
+	imageHeight = image.height;
+	imageY = -1;
+	lineArray = new byte[imageWidth];
+	imageX = imageWidth + 1; // Force a read
+}
+/**
+ * Answer the next code from the input byte array.
+ */
+int nextCode() {
+	int code;
+	if (bitsLeft == 0) {
+		if (blockIndex >= blockSize) {
+			blockSize = readBlock();
+			blockIndex = 0;
+			if (blockSize == 0) return endCode;
+		}
+		blockIndex++;
+		currentByte = block[blockIndex] & 0xFF;
+		bitsLeft = 8;
+		code = currentByte;
+	} else {
+		int shift = bitsLeft - 8;
+		if (shift < 0)
+			code = currentByte >> (0 - shift);
+		else
+			code = currentByte << shift;
+	}
+	while (codeSize > bitsLeft) {
+		if (blockIndex >= blockSize) {
+			blockSize = readBlock();
+			blockIndex = 0;
+			if (blockSize == 0) return endCode;
+		}
+		blockIndex++;
+		currentByte = block[blockIndex] & 0xFF;
+		code += currentByte << bitsLeft;
+		bitsLeft += 8;
+	}
+	bitsLeft -= codeSize;
+	return code & codeMask;
+}
+/**
+ * Answer the next pixel to encode in the image
+ */
+int nextPixel() {
+	imageX++;
+	if (imageX > imageWidth) {
+		imageY++;
+		if (imageY >= imageHeight) {
+			return -1;
+		} else {
+			nextPixels(lineArray, imageWidth);
+		}
+		imageX = 1;
+	}
+	return this.lineArray[imageX - 1] & 0xFF;
+}
+/**
+ * Copy a row of pixel values from the image.
+ */
+void nextPixels(byte[] buf, int lineWidth) {
+	if (image.depth == 8) {
+        int start = imageY * image.bytesPerLine;
+        buf[ 0 .. lineWidth ] = image.data[ start .. start + lineWidth ];
+	} else {
+		image.getPixels(0, imageY, lineWidth, buf, 0);
+	}
+}
+/**
+ * Output aCode to the output stream.
+ */
+void nextPutCode(int aCode) {
+	int codeToDo = aCode;
+	int codeBitsToDo = codeSize;
+	// Fill in the remainder of the current byte with the
+	// *high-order* bits of the code.
+	int c = codeToDo & MASK_TABLE[bitsLeft - 1];
+	currentByte = currentByte | (c << (8 - bitsLeft));
+	block[blockIndex] = cast(byte)currentByte;
+	codeBitsToDo -= bitsLeft;
+	if (codeBitsToDo < 1) {
+		// The whole code fit in the first byte, so we are done.
+		bitsLeft -= codeSize;
+		if (bitsLeft == 0) {
+			// We used the whole last byte, so get ready
+			// for the next one.
+			bitsLeft = 8;
+			blockIndex++;
+			if (blockIndex >= blockSize) {
+				writeBlock();
+				blockIndex = 1;
+			}
+			currentByte = 0;
+		}
+		return;
+	}
+	codeToDo = codeToDo >> bitsLeft;
+
+	// Fill in any remaining whole bytes (i.e. not the last one!)
+	blockIndex++;
+	if (blockIndex >= blockSize) {
+		writeBlock();
+		blockIndex = 1;
+	}
+	while (codeBitsToDo >= 8) {
+		currentByte = codeToDo & 0xFF;
+		block[blockIndex] = cast(byte)currentByte;
+		codeToDo = codeToDo >> 8;
+		codeBitsToDo -= 8;
+		blockIndex++;
+		if (blockIndex >= blockSize) {
+			writeBlock();
+			blockIndex = 1;
+		}
+	}
+	// Fill the *low-order* bits of the last byte with the remainder
+	bitsLeft = 8 - codeBitsToDo;
+	currentByte = codeToDo;
+	block[blockIndex] = cast(byte)currentByte;
+}
+/**
+ * Copy a row of pixel values to the image.
+ */
+void nextPutPixels(byte[] buf) {
+	if (image.depth == 8) {
+		// Slight optimization for depth = 8.
+		int start = line * image.bytesPerLine;
+		for (int i = 0; i < imageWidth; i++)
+			image.data[start + i] = buf[i];
+	} else {
+		image.setPixels(0, line, imageWidth, buf, 0);
+	}
+	if (interlaced) {
+		if (pass == 1) {
+			copyRow(buf, 7);
+			line += 8;
+		} else if (pass == 2) {
+			copyRow(buf, 3);
+			line += 8;
+		} else if (pass == 3) {
+			copyRow(buf, 1);
+			line += 4;
+		} else if (pass == 4) {
+			line += 2;
+		} else if (pass == 5) {
+			line += 0;
+		}
+		if (line >= imageHeight) {
+			pass++;
+			if (pass == 2) line = 4;
+			else if (pass == 3) line = 2;
+			else if (pass == 4) line = 1;
+			else if (pass == 5) line = 0;
+			if (pass < 5) {
+				if (loader.hasListeners()) {
+					ImageData imageCopy = cast(ImageData) image.clone();
+					loader.notifyListeners(
+						new ImageLoaderEvent(loader, imageCopy, pass - 2, false));
+				}
+			}
+		}
+		if (line >= imageHeight) line = 0;
+	} else {
+		line++;
+	}
+}
+/**
+ * Copy duplicate rows of pixel values to the image.
+ * This is to fill in rows if the image is interlaced.
+ */
+void copyRow(byte[] buf, int copies) {
+	for (int i = 1; i <= copies; i++) {
+		if (line + i < imageHeight) {
+			image.setPixels(0, line + i, imageWidth, buf, 0);
+		}
+	}
+}
+/**
+ * Read a block from the byte stream.
+ * Return the number of bytes read.
+ * Throw an exception if the block could not be read.
+ */
+int readBlock() {
+	int size = -1;
+	try {
+		size = inputStream.read();
+		if (size == -1) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		block[0] = cast(byte)size;
+		size = inputStream.read(block, 1, size);
+		if (size == -1) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+	} catch (TracedException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return size;
+}
+/**
+ * Write a block to the byte stream.
+ * Throw an exception if the block could not be written.
+ */
+void writeBlock() {
+	try {
+		outputStream.write(block, 0, (block[0] & 0xFF) + 1);
+	} catch (TracedException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/LZWNode.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.LZWNode;
+
+
+final class LZWNode {
+	public LZWNode left, right, children;
+	public int code, prefix, suffix;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/OS2BMPFileFormat.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.OS2BMPFileFormat;
+
+import dwt.SWT;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoader;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.internal.image.LEDataInputStream;
+import dwt.internal.image.FileFormat;
+import dwt.dwthelper.ByteArrayOutputStream;
+
+import tango.core.Exception;
+
+final class OS2BMPFileFormat : FileFormat {
+	static final int BMPFileHeaderSize = 14;
+	static final int BMPHeaderFixedSize = 12;
+	int width, height, bitCount;
+
+bool isFileFormat(LEDataInputStream stream) {
+	try {
+		byte[] header = new byte[18];
+		stream.read(header);
+		stream.unread(header);
+		int infoHeaderSize = (header[14] & 0xFF) | ((header[15] & 0xFF) << 8) | ((header[16] & 0xFF) << 16) | ((header[17] & 0xFF) << 24);
+		return header[0] == 0x42 && header[1] == 0x4D && infoHeaderSize == BMPHeaderFixedSize;
+	} catch (TracedException e) {
+		return false;
+	}
+}
+byte[] loadData(byte[] infoHeader) {
+	int stride = (width * bitCount + 7) / 8;
+	stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
+	byte[] data = loadData(infoHeader, stride);
+	flipScanLines(data, stride, height);
+	return data;
+}
+byte[] loadData(byte[] infoHeader, int stride) {
+	int dataSize = height * stride;
+	byte[] data = new byte[dataSize];
+	try {
+		if (inputStream.read(data) != dataSize)
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return data;
+}
+int[] loadFileHeader() {
+	int[] header = new int[5];
+	try {
+		header[0] = inputStream.readShort();
+		header[1] = inputStream.readInt();
+		header[2] = inputStream.readShort();
+		header[3] = inputStream.readShort();
+		header[4] = inputStream.readInt();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	if (header[0] != 0x4D42)
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	return header;
+}
+ImageData[] loadFromByteStream() {
+	int[] fileHeader = loadFileHeader();
+	byte[] infoHeader = new byte[BMPHeaderFixedSize];
+	try {
+		inputStream.read(infoHeader);
+	} catch (TracedException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8);
+	height = (infoHeader[6] & 0xFF) | ((infoHeader[7] & 0xFF) << 8);
+	bitCount = (infoHeader[10] & 0xFF) | ((infoHeader[11] & 0xFF) << 8);
+	PaletteData palette = loadPalette(infoHeader);
+	if (inputStream.getPosition() < fileHeader[4]) {
+		// Seek to the specified offset
+		try {
+			inputStream.skip(fileHeader[4] - inputStream.getPosition());
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+	byte[] data = loadData(infoHeader);
+	int type = SWT.IMAGE_OS2_BMP;
+	return [
+		ImageData.internal_new(
+			width,
+			height,
+			bitCount,
+			palette,
+			4,
+			data,
+			0,
+			null,
+			null,
+			-1,
+			-1,
+			type,
+			0,
+			0,
+			0,
+			0)
+	];
+}
+PaletteData loadPalette(byte[] infoHeader) {
+	if (bitCount <= 8) {
+		int numColors = 1 << bitCount;
+		byte[] buf = new byte[numColors * 3];
+		try {
+			if (inputStream.read(buf) != buf.length)
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		return paletteFromBytes(buf, numColors);
+	}
+	if (bitCount == 16) return new PaletteData(0x7C00, 0x3E0, 0x1F);
+	if (bitCount == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
+	return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
+}
+PaletteData paletteFromBytes(byte[] bytes, int numColors) {
+	int bytesOffset = 0;
+	RGB[] colors = new RGB[numColors];
+	for (int i = 0; i < numColors; i++) {
+		colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
+			bytes[bytesOffset + 1] & 0xFF,
+			bytes[bytesOffset] & 0xFF);
+		bytesOffset += 3;
+	}
+	return new PaletteData(colors);
+}
+/**
+ * Answer a byte array containing the BMP representation of
+ * the given device independent palette.
+ */
+static byte[] paletteToBytes(PaletteData pal) {
+	int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256);
+	byte[] bytes = new byte[n * 3];
+	int offset = 0;
+	for (int i = 0; i < n; i++) {
+		RGB col = pal.colors[i];
+		bytes[offset] = cast(byte)col.blue;
+		bytes[offset + 1] = cast(byte)col.green;
+		bytes[offset + 2] = cast(byte)col.red;
+		offset += 3;
+	}
+	return bytes;
+}
+/**
+ * Unload the given image's data into the given byte stream.
+ * Answer the number of bytes written.
+ */
+int unloadData(ImageData image, OutputStream ostr) {
+	int bmpBpl = 0;
+	try {
+		int bpl = (image.width * image.depth + 7) / 8;
+		bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
+		int linesPerBuf = 32678 / bmpBpl;
+		byte[] buf = new byte[linesPerBuf * bmpBpl];
+		byte[] data = image.data;
+		int imageBpl = image.bytesPerLine;
+		int dataIndex = imageBpl * (image.height - 1); // Start at last line
+		if (image.depth == 16) {
+			for (int y = 0; y < image.height; y += linesPerBuf) {
+				int count = image.height - y;
+				if (linesPerBuf < count) count = linesPerBuf;
+				int bufOffset = 0;
+				for (int i = 0; i < count; i++) {
+					for (int wIndex = 0; wIndex < bpl; wIndex += 2) {
+						buf[bufOffset + wIndex + 1] = data[dataIndex + wIndex + 1];
+						buf[bufOffset + wIndex] = data[dataIndex + wIndex];
+					}
+					bufOffset += bmpBpl;
+					dataIndex -= imageBpl;
+				}
+				ostr.write(buf, 0, bufOffset);
+			}
+		} else {
+			for (int y = 0; y < image.height; y += linesPerBuf) {
+				int tmp = image.height - y;
+				int count = tmp < linesPerBuf ? tmp : linesPerBuf;
+				int bufOffset = 0;
+				for (int i = 0; i < count; i++) {
+                    buf[ bufOffset .. bufOffset+bpl ] = data[ dataIndex .. dataIndex+bpl ];
+					bufOffset += bmpBpl;
+					dataIndex -= imageBpl;
+				}
+				ostr.write(buf, 0, bufOffset);
+			}
+		}
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return bmpBpl * image.height;
+}
+/**
+ * Unload a DeviceIndependentImage using Windows .BMP format into the given
+ * byte stream.
+ */
+void unloadIntoByteStream(ImageLoader loader) {
+	ImageData image = loader.data[0];
+	byte[] rgbs;
+	int numCols;
+	if (!((image.depth == 1) || (image.depth == 4) || (image.depth == 8) ||
+		  (image.depth == 16) || (image.depth == 24) || (image.depth == 32)))
+			SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+	PaletteData pal = image.palette;
+	if ((image.depth == 16) || (image.depth == 24) || (image.depth == 32)) {
+		if (!pal.isDirect)
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		numCols = 0;
+		rgbs = null;
+	} else {
+		if (pal.isDirect)
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		numCols = pal.colors.length;
+		rgbs = paletteToBytes(pal);
+	}
+	// Fill in file header, except for bfsize, which is done later.
+	int headersSize = BMPFileHeaderSize + BMPHeaderFixedSize;
+	int[] fileHeader = new int[5];
+	fileHeader[0] = 0x4D42;	// Signature
+	fileHeader[1] = 0; // File size - filled in later
+	fileHeader[2] = 0; // Reserved 1
+	fileHeader[3] = 0; // Reserved 2
+	fileHeader[4] = headersSize; // Offset to data
+	if (rgbs != null) {
+		fileHeader[4] += rgbs.length;
+	}
+
+	// Prepare data. This is done first so we don't have to try to rewind
+	// the stream and fill in the details later.
+	ByteArrayOutputStream ostr = new ByteArrayOutputStream();
+	unloadData(image, ostr);
+	byte[] data = ostr.toByteArray();
+
+	// Calculate file size
+	fileHeader[1] = fileHeader[4] + data.length;
+
+	// Write the headers
+	try {
+		outputStream.writeShort(fileHeader[0]);
+		outputStream.writeInt(fileHeader[1]);
+		outputStream.writeShort(fileHeader[2]);
+		outputStream.writeShort(fileHeader[3]);
+		outputStream.writeInt(fileHeader[4]);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	try {
+		outputStream.writeInt(BMPHeaderFixedSize);
+		outputStream.writeShort(image.width);
+		outputStream.writeShort(image.height);
+		outputStream.writeShort(1);
+		outputStream.writeShort(cast(short)image.depth);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+
+	// Unload palette
+	if (numCols > 0) {
+		try {
+			outputStream.write(rgbs);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+
+	// Unload the data
+	try {
+		outputStream.write(data);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+void flipScanLines(byte[] data, int stride, int height) {
+	int i1 = 0;
+	int i2 = (height - 1) * stride;
+	for (int i = 0; i < height / 2; i++) {
+		for (int index = 0; index < stride; index++) {
+			byte b = data[index + i1];
+			data[index + i1] = data[index + i2];
+			data[index + i2] = b;
+		}
+		i1 += stride;
+		i2 -= stride;
+	}
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/TIFFDirectory.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,635 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.TIFFDirectory;
+
+import dwt.internal.image.TIFFRandomFileAccess;
+import dwt.internal.image.TIFFModifiedHuffmanCodec;
+import dwt.internal.image.LEDataOutputStream;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoaderEvent;
+import dwt.graphics.ImageLoader;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.SWT;
+import dwt.dwthelper.Integer;
+
+//PORTING_TYPE
+class Image{}
+
+final class TIFFDirectory {
+
+	TIFFRandomFileAccess file;
+	bool isLittleEndian;
+	ImageLoader loader;
+	int depth;
+
+	/* Directory fields */
+	int imageWidth;
+	int imageLength;
+	int[] bitsPerSample;
+	int compression;
+	int photometricInterpretation;
+	int[] stripOffsets;
+	int samplesPerPixel;
+	int rowsPerStrip;
+	int[] stripByteCounts;
+	int t4Options;
+	int colorMapOffset;
+
+	/* Encoder fields */
+	ImageData image;
+	LEDataOutputStream ostr;
+
+	static const int NO_VALUE = -1;
+
+	static const short TAG_ImageWidth = 256;
+	static const short TAG_ImageLength = 257;
+	static const short TAG_BitsPerSample = 258;
+	static const short TAG_Compression = 259;
+	static const short TAG_PhotometricInterpretation = 262;
+	static const short TAG_StripOffsets = 273;
+	static const short TAG_SamplesPerPixel = 277;
+	static const short TAG_RowsPerStrip = 278;
+	static const short TAG_StripByteCounts = 279;
+	static const short TAG_XResolution = 282;
+	static const short TAG_YResolution = 283;
+	static const short TAG_T4Options = 292;
+	static const short TAG_ResolutionUnit = 296;
+	static const short TAG_ColorMap = 320;
+
+	static const int TYPE_BYTE = 1;
+	static const int TYPE_ASCII = 2;
+	static const int TYPE_SHORT = 3;
+	static const int TYPE_LONG = 4;
+	static const int TYPE_RATIONAL = 5;
+
+	/* Different compression schemes */
+	static const int COMPRESSION_NONE = 1;
+	static const int COMPRESSION_CCITT_3_1 = 2;
+	static const int COMPRESSION_PACKBITS = 32773;
+
+	static const int IFD_ENTRY_SIZE = 12;
+
+public this(TIFFRandomFileAccess file, bool isLittleEndian, ImageLoader loader) {
+	this.file = file;
+	this.isLittleEndian = isLittleEndian;
+	this.loader = loader;
+}
+
+public this(ImageData image) {
+	this.image = image;
+}
+
+/* PackBits decoder */
+int decodePackBits(byte[] src, byte[] dest, int offsetDest) {
+	int destIndex = offsetDest;
+	int srcIndex = 0;
+	while (srcIndex < src.length) {
+		byte n = src[srcIndex];
+		if (0 <= n && n <= 127) {
+			/* Copy next n+1 bytes literally */
+            ++srcIndex;
+            dest[ destIndex .. destIndex + n + 1 ] = src[ srcIndex .. srcIndex + n + 1 ];
+			srcIndex += n + 1;
+			destIndex += n + 1;
+		} else if (-127 <= n && n <= -1) {
+			/* Copy next byte -n+1 times */
+			byte value = src[++srcIndex];
+			for (int j = 0; j < -n + 1; j++) {
+				dest[destIndex++] = value;
+			}
+			srcIndex++;
+		} else {
+			/* Noop when n == -128 */
+			srcIndex++;
+		}
+	}
+	/* Number of bytes copied */
+	return destIndex - offsetDest;
+}
+
+int getEntryValue(int type, byte[] buffer, int index) {
+	return toInt(buffer, index + 8, type);
+}
+
+void getEntryValue(int type, byte[] buffer, int index, int[] values)  {
+	int start = index + 8;
+	int size;
+	int offset = toInt(buffer, start, TYPE_LONG);
+	switch (type) {
+		case TYPE_SHORT: size = 2; break;
+		case TYPE_LONG: size = 4; break;
+		case TYPE_RATIONAL: size = 8; break;
+		case TYPE_ASCII:
+		case TYPE_BYTE: size = 1; break;
+		default: SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); return;
+	}
+	if (values.length * size > 4) {
+		buffer = new byte[values.length * size];
+		file.seek(offset);
+		file.read(buffer);
+		start = 0;
+	}
+	for (int i = 0; i < values.length; i++) {
+		values[i] = toInt(buffer, start + i * size, type);
+	}
+}
+
+void decodePixels(ImageData image)  {
+	/* Each row is byte aligned */
+	byte[] imageData = new byte[(imageWidth * depth + 7) / 8 * imageLength];
+	image.data = imageData;
+	int destIndex = 0;
+	int length = stripOffsets.length;
+	for (int i = 0; i < length; i++) {
+		/* Read a strip */
+		byte[] data = new byte[](stripByteCounts[i]);
+		file.seek(stripOffsets[i]);
+		file.read(data);
+		if (compression == COMPRESSION_NONE) {
+            imageData[ destIndex .. destIndex+data.length] = data[];
+			destIndex += data.length;
+		} else if (compression == COMPRESSION_PACKBITS) {
+			destIndex += decodePackBits(data, imageData, destIndex);
+		} else if (compression == COMPRESSION_CCITT_3_1 || compression == 3) {
+			TIFFModifiedHuffmanCodec codec = new TIFFModifiedHuffmanCodec();
+			int nRows = rowsPerStrip;
+			if (i == length -1) {
+				int n = imageLength % rowsPerStrip;
+				if (n != 0) nRows = n;
+			}
+			destIndex += codec.decode(data, imageData, destIndex, imageWidth, nRows);
+		}
+		if (loader.hasListeners()) {
+			loader.notifyListeners(new ImageLoaderEvent(loader, image, i, i == length - 1));
+		}
+	}
+}
+
+PaletteData getColorMap()  {
+	int numColors = 1 << bitsPerSample[0];
+	/* R, G, B entries are 16 bit wide (2 bytes) */
+	int numBytes = 3 * 2 * numColors;
+	byte[] buffer = new byte[numBytes];
+	file.seek(colorMapOffset);
+	file.read(buffer);
+	RGB[] colors = new RGB[numColors];
+	/**
+	 * 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.
+	 */
+	int offset = isLittleEndian ? 1 : 0;
+	int startG = 2 * numColors;
+	int startB = startG + 2 * numColors;
+	for (int i = 0; i < numColors; i++) {
+		int r = buffer[offset] & 0xFF;
+		int g = buffer[startG + offset] & 0xFF;
+		int b = buffer[startB + offset] & 0xFF;
+		colors[i] = new RGB(r, g, b);
+		offset += 2;
+	}
+	return new PaletteData(colors);
+}
+
+PaletteData getGrayPalette() {
+	int numColors = 1 << bitsPerSample[0];
+	RGB[] rgbs = new RGB[numColors];
+	for (int i = 0; i < numColors; i++) {
+		int value = i * 0xFF / (numColors - 1);
+		if (photometricInterpretation == 0) value = 0xFF - value;
+		rgbs[i] = new RGB(value, value, value);
+	}
+	return new PaletteData(rgbs);
+}
+
+PaletteData getRGBPalette(int bitsR, int bitsG, int bitsB) {
+	int blueMask = 0;
+	for (int i = 0; i < bitsB; i++) {
+		blueMask |= 1 << i;
+	}
+	int greenMask = 0;
+	for (int i = bitsB; i < bitsB + bitsG; i++) {
+		greenMask |= 1 << i;
+	}
+	int redMask = 0;
+	for (int i = bitsB + bitsG; i < bitsB + bitsG + bitsR; i++) {
+		redMask |= 1 << i;
+	}
+	return new PaletteData(redMask, greenMask, blueMask);
+}
+
+int formatStrips(int rowByteSize, int nbrRows, byte[] data, int maxStripByteSize, int offsetPostIFD, int extraBytes, int[][] strips) {
+	/*
+	* Calculate the nbr of required strips given the following requirements:
+	* - each strip should, if possible, not be greater than maxStripByteSize
+	* - each strip should contain 1 or more entire rows
+	*
+	* Format the strip fields arrays so that the image data is stored in one
+	* contiguous block. This block is stored after the IFD and after any tag
+	* info described in the IFD.
+	*/
+	int n, nbrRowsPerStrip;
+	if (rowByteSize > maxStripByteSize) {
+		/* Each strip contains 1 row */
+		n = data.length / rowByteSize;
+		nbrRowsPerStrip = 1;
+	} else {
+		int nbr = (data.length + maxStripByteSize - 1) / maxStripByteSize;
+		nbrRowsPerStrip = nbrRows / nbr;
+		n = (nbrRows + nbrRowsPerStrip - 1) / nbrRowsPerStrip;
+	}
+	int stripByteSize = rowByteSize * nbrRowsPerStrip;
+
+	int[] offsets = new int[n];
+	int[] counts = new int[n];
+	/*
+	* Nbr of bytes between the end of the IFD directory and the start of
+	* the image data. Keep space for at least the offsets and counts
+	* data, each field being TYPE_LONG (4 bytes). If other tags require
+	* space between the IFD and the image block, use the extraBytes
+	* parameter.
+	* If there is only one strip, the offsets and counts data is stored
+	* directly in the IFD and we need not reserve space for it.
+	*/
+	int postIFDData = n == 1 ? 0 : n * 2 * 4;
+	int startOffset = offsetPostIFD + extraBytes + postIFDData; /* offset of image data */
+
+	int offset = startOffset;
+	for (int i = 0; i < n; i++) {
+		/*
+		* Store all strips sequentially to allow us
+		* to copy all pixels in one contiguous area.
+		*/
+		offsets[i] = offset;
+		counts[i] = stripByteSize;
+		offset += stripByteSize;
+	}
+	/* The last strip may contain fewer rows */
+	int mod = data.length % stripByteSize;
+	if (mod != 0) counts[counts.length - 1] = mod;
+
+	strips[0] = offsets;
+	strips[1] = counts;
+	return nbrRowsPerStrip;
+}
+
+int[] formatColorMap(RGB[] rgbs) {
+	/*
+	* In a TIFF ColorMap, all red come first, followed by
+	* green and blue. All values must be converted from
+	* 8 bit to 16 bit.
+	*/
+	int[] colorMap = new int[rgbs.length * 3];
+	int offsetGreen = rgbs.length;
+	int offsetBlue = rgbs.length * 2;
+	for (int i = 0; i < rgbs.length; i++) {
+		colorMap[i] = rgbs[i].red << 8 | rgbs[i].red;
+		colorMap[i + offsetGreen] = rgbs[i].green << 8 | rgbs[i].green;
+		colorMap[i + offsetBlue] = rgbs[i].blue << 8 | rgbs[i].blue;
+	}
+	return colorMap;
+}
+
+void parseEntries(byte[] buffer)  {
+	for (int offset = 0; offset < buffer.length; offset += IFD_ENTRY_SIZE) {
+		int tag = toInt(buffer, offset, TYPE_SHORT);
+		int type = toInt(buffer, offset + 2, TYPE_SHORT);
+		int count = toInt(buffer, offset + 4, TYPE_LONG);
+		switch (tag) {
+			case TAG_ImageWidth: {
+				imageWidth = getEntryValue(type, buffer, offset);
+				break;
+			}
+			case TAG_ImageLength: {
+				imageLength = getEntryValue(type, buffer, offset);
+				break;
+			}
+			case TAG_BitsPerSample: {
+				if (type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE);
+				bitsPerSample = new int[count];
+				getEntryValue(type, buffer, offset, bitsPerSample);
+				break;
+			}
+			case TAG_Compression: {
+				compression = getEntryValue(type, buffer, offset);
+				break;
+			}
+			case TAG_PhotometricInterpretation: {
+				photometricInterpretation = getEntryValue(type, buffer, offset);
+				break;
+			}
+			case TAG_StripOffsets: {
+				if (type != TYPE_LONG && type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE);
+				stripOffsets = new int[count];
+				getEntryValue(type, buffer, offset, stripOffsets);
+				break;
+			}
+			case TAG_SamplesPerPixel: {
+				if (type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE);
+				samplesPerPixel = getEntryValue(type, buffer, offset);
+				/* Only the basic 1 and 3 values are supported */
+				if (samplesPerPixel != 1 && samplesPerPixel != 3) SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+				break;
+			}
+			case TAG_RowsPerStrip: {
+				rowsPerStrip = getEntryValue(type, buffer, offset);
+				break;
+			}
+			case TAG_StripByteCounts: {
+				stripByteCounts = new int[count];
+				getEntryValue(type, buffer, offset, stripByteCounts);
+				break;
+			}
+			case TAG_XResolution: {
+				/* Ignored */
+				break;
+			}
+			case TAG_YResolution: {
+				/* Ignored */
+				break;
+			}
+			case TAG_T4Options: {
+				if (type != TYPE_LONG) SWT.error(SWT.ERROR_INVALID_IMAGE);
+				t4Options = getEntryValue(type, buffer, offset);
+				if ((t4Options & 0x1) == 1) {
+					/* 2-dimensional coding is not supported */
+					SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+				}
+				break;
+			}
+			case TAG_ResolutionUnit: {
+				/* Ignored */
+				break;
+			}
+			case TAG_ColorMap: {
+				if (type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE);
+				/* Get the offset of the colorMap (use TYPE_LONG) */
+				colorMapOffset = getEntryValue(TYPE_LONG, buffer, offset);
+				break;
+			}
+		}
+	}
+}
+
+public ImageData read()  {
+	/* Set TIFF default values */
+	bitsPerSample = [1];
+	colorMapOffset = NO_VALUE;
+	compression = 1;
+	imageLength = NO_VALUE;
+	imageWidth = NO_VALUE;
+	photometricInterpretation = NO_VALUE;
+	rowsPerStrip = Integer.MAX_VALUE;
+	samplesPerPixel = 1;
+	stripByteCounts = null;
+	stripOffsets = null;
+
+	byte[] buffer = new byte[2];
+	file.read(buffer);
+	int numberEntries = toInt(buffer, 0, TYPE_SHORT);
+	buffer = new byte[IFD_ENTRY_SIZE * numberEntries];
+	file.read(buffer);
+	parseEntries(buffer);
+
+	PaletteData palette = null;
+	depth = 0;
+	switch (photometricInterpretation) {
+		case 0:
+		case 1: {
+			/* Bilevel or Grayscale image */
+			palette = getGrayPalette();
+			depth = bitsPerSample[0];
+			break;
+		}
+		case 2: {
+			/* RGB image */
+			if (colorMapOffset != NO_VALUE) SWT.error(SWT.ERROR_INVALID_IMAGE);
+			/* SamplesPerPixel 3 is the only value supported */
+			palette = getRGBPalette(bitsPerSample[0], bitsPerSample[1], bitsPerSample[2]);
+			depth = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2];
+			break;
+		}
+		case 3: {
+			/* Palette Color image */
+			if (colorMapOffset == NO_VALUE) SWT.error(SWT.ERROR_INVALID_IMAGE);
+			palette = getColorMap();
+			depth = bitsPerSample[0];
+			break;
+		}
+		default: {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+	}
+
+	ImageData image = ImageData.internal_new(
+			imageWidth,
+			imageLength,
+			depth,
+			palette,
+			1,
+			null,
+			0,
+			null,
+			null,
+			-1,
+			-1,
+			SWT.IMAGE_TIFF,
+			0,
+			0,
+			0,
+			0);
+	decodePixels(image);
+	return image;
+}
+
+int toInt(byte[] buffer, int i, int type) {
+	if (type == TYPE_LONG) {
+		return isLittleEndian ?
+		(buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) | ((buffer[i + 2] & 0xFF) << 16) | ((buffer[i + 3] & 0xFF) << 24) :
+		(buffer[i + 3] & 0xFF) | ((buffer[i + 2] & 0xFF) << 8) | ((buffer[i + 1] & 0xFF) << 16) | ((buffer[i] & 0xFF) << 24);
+	}
+	if (type == TYPE_SHORT) {
+		return isLittleEndian ?
+		(buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) :
+		(buffer[i + 1] & 0xFF) | ((buffer[i] & 0xFF) << 8);
+	}
+	/* Invalid type */
+	SWT.error(SWT.ERROR_INVALID_IMAGE);
+	return -1;
+}
+
+void write(int photometricInterpretation)  {
+	bool isRGB = photometricInterpretation == 2;
+	bool isColorMap = photometricInterpretation == 3;
+	bool isBiLevel = photometricInterpretation == 0 || photometricInterpretation == 1;
+
+	int imageWidth = image.width;
+	int imageLength = image.height;
+	int rowByteSize = image.bytesPerLine;
+
+	int numberEntries = isBiLevel ? 9 : 11;
+	int lengthDirectory = 2 + 12 * numberEntries + 4;
+	/* Offset following the header and the directory */
+	int nextOffset = 8 + lengthDirectory;
+
+	/* Extra space used by XResolution and YResolution values */
+	int extraBytes = 16;
+
+	int[] colorMap = null;
+	if (isColorMap) {
+		PaletteData palette = image.palette;
+		RGB[] rgbs = palette.getRGBs();
+		colorMap = formatColorMap(rgbs);
+		/* The number of entries of the Color Map must match the bitsPerSample field */
+		if (colorMap.length != 3 * 1 << image.depth) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+		/* Extra space used by ColorMap values */
+		extraBytes += colorMap.length * 2;
+	}
+	if (isRGB) {
+		/* Extra space used by BitsPerSample values */
+		extraBytes += 6;
+	}
+	/* TIFF recommends storing the data in strips of no more than 8 Ko */
+	byte[] data = image.data;
+	int[][] strips = new int[][](2);
+	int nbrRowsPerStrip = formatStrips(rowByteSize, imageLength, data, 8192, nextOffset, extraBytes, strips);
+	int[] stripOffsets = strips[0];
+	int[] stripByteCounts = strips[1];
+
+	int bitsPerSampleOffset = NO_VALUE;
+	if (isRGB) {
+		bitsPerSampleOffset = nextOffset;
+		nextOffset += 6;
+	}
+	int stripOffsetsOffset = NO_VALUE, stripByteCountsOffset = NO_VALUE;
+	int xResolutionOffset, yResolutionOffset, colorMapOffset = NO_VALUE;
+	int cnt = stripOffsets.length;
+	if (cnt > 1) {
+		stripOffsetsOffset = nextOffset;
+		nextOffset += 4 * cnt;
+		stripByteCountsOffset = nextOffset;
+		nextOffset += 4 * cnt;
+	}
+	xResolutionOffset = nextOffset;
+	nextOffset += 8;
+	yResolutionOffset = nextOffset;
+	nextOffset += 8;
+	if (isColorMap) {
+		colorMapOffset = nextOffset;
+		nextOffset += colorMap.length * 2;
+	}
+	/* TIFF header */
+	writeHeader();
+
+	/* Image File Directory */
+	ostr.writeShort(numberEntries);
+	writeEntry(TAG_ImageWidth, TYPE_LONG, 1, imageWidth);
+	writeEntry(TAG_ImageLength, TYPE_LONG, 1, imageLength);
+	if (isColorMap) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 1, image.depth);
+	if (isRGB) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 3, bitsPerSampleOffset);
+	writeEntry(TAG_Compression, TYPE_SHORT, 1, COMPRESSION_NONE);
+	writeEntry(TAG_PhotometricInterpretation, TYPE_SHORT, 1, photometricInterpretation);
+	writeEntry(TAG_StripOffsets, TYPE_LONG, cnt, cnt > 1 ? stripOffsetsOffset : stripOffsets[0]);
+	if (isRGB) writeEntry(TAG_SamplesPerPixel, TYPE_SHORT, 1, 3);
+	writeEntry(TAG_RowsPerStrip, TYPE_LONG, 1, nbrRowsPerStrip);
+	writeEntry(TAG_StripByteCounts, TYPE_LONG, cnt, cnt > 1 ? stripByteCountsOffset : stripByteCounts[0]);
+	writeEntry(TAG_XResolution, TYPE_RATIONAL, 1, xResolutionOffset);
+	writeEntry(TAG_YResolution, TYPE_RATIONAL, 1, yResolutionOffset);
+	if (isColorMap) writeEntry(TAG_ColorMap, TYPE_SHORT, colorMap.length, colorMapOffset);
+	/* Offset of next IFD (0 for last IFD) */
+	ostr.writeInt(0);
+
+	/* Values longer than 4 bytes Section */
+
+	/* BitsPerSample 8,8,8 */
+	if (isRGB) for (int i = 0; i < 3; i++) ostr.writeShort(8);
+	if (cnt > 1) {
+		for (int i = 0; i < cnt; i++) ostr.writeInt(stripOffsets[i]);
+		for (int i = 0; i < cnt; i++) ostr.writeInt(stripByteCounts[i]);
+	}
+	/* XResolution and YResolution set to 300 dpi */
+	for (int i = 0; i < 2; i++) {
+		ostr.writeInt(300);
+		ostr.writeInt(1);
+	}
+	/* ColorMap */
+	if (isColorMap) for (int i = 0; i < colorMap.length; i++) ostr.writeShort(colorMap[i]);
+
+	/* Image Data */
+	ostr.write(data);
+}
+
+void writeEntry(short tag, int type, int count, int value) {
+	ostr.writeShort(tag);
+	ostr.writeShort(type);
+	ostr.writeInt(count);
+	ostr.writeInt(value);
+}
+
+void writeHeader() {
+	/* little endian */
+	ostr.write(0x49);
+	ostr.write(0x49);
+
+	/* TIFF identifier */
+	ostr.writeShort(42);
+	/*
+	* Offset of the first IFD is chosen to be 8.
+	* It is word aligned and immediately after this header.
+	*/
+	ostr.writeInt(8);
+}
+
+void writeToStream(LEDataOutputStream byteStream) {
+	ostr = byteStream;
+	int photometricInterpretation = -1;
+
+	/* Scanline pad must be 1 */
+	if (image.scanlinePad != 1) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+	switch (image.depth) {
+		case 1: {
+			/* Palette must be black and white or white and black */
+			PaletteData palette = image.palette;
+			RGB[] rgbs = palette.colors;
+			if (palette.isDirect || rgbs == null || rgbs.length != 2) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+			RGB rgb0 = rgbs[0];
+			RGB rgb1 = rgbs[1];
+			if (!(rgb0.red == rgb0.green && rgb0.green == rgb0.blue &&
+				rgb1.red == rgb1.green && rgb1.green == rgb1.blue &&
+				((rgb0.red == 0x0 && rgb1.red == 0xFF) || (rgb0.red == 0xFF && rgb1.red == 0x0)))) {
+				SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+			}
+			/* 0 means a color index of 0 is imaged as white */
+			photometricInterpretation = image.palette.colors[0].red == 0xFF ? 0 : 1;
+			break;
+		}
+		case 4:
+		case 8: {
+			photometricInterpretation = 3;
+			break;
+		}
+		case 24: {
+			photometricInterpretation = 2;
+			break;
+		}
+		default: {
+			SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+		}
+	}
+	write(photometricInterpretation);
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/TIFFFileFormat.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module dwt.internal.image.TIFFFileFormat;
+
+import dwt.internal.image.TIFFRandomFileAccess;
+import dwt.internal.image.TIFFDirectory;
+import dwt.SWT;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoader;
+import dwt.internal.image.FileFormat;
+
+import tango.core.Exception;
+
+/**
+ * Baseline TIFF decoder revision 6.0
+ * Extension T4-encoding CCITT T.4 1D
+ */
+final class TIFFFileFormat : FileFormat {
+
+bool isFileFormat(LEDataInputStream stream) {
+	try {
+		byte[] header = new byte[4];
+		stream.read(header);
+		stream.unread(header);
+		if (header[0] != header[1]) return false;
+		if (!(header[0] == 0x49 && header[2] == 42 && header[3] == 0) &&
+			!(header[0] == 0x4d && header[2] == 0 && header[3] == 42)) {
+			return false;
+		}
+		return true;
+	} catch (TracedException e) {
+		return false;
+	}
+}
+
+ImageData[] loadFromByteStream() {
+	byte[] header = new byte[8];
+	bool isLittleEndian;
+	ImageData[] images = new ImageData[0];
+	TIFFRandomFileAccess file = new TIFFRandomFileAccess(inputStream);
+	try {
+		file.read(header);
+		if (header[0] != header[1]) SWT.error(SWT.ERROR_INVALID_IMAGE);
+		if (!(header[0] == 0x49 && header[2] == 42 && header[3] == 0) &&
+			!(header[0] == 0x4d && header[2] == 0 && header[3] == 42)) {
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		}
+		isLittleEndian = header[0] == 0x49;
+		int offset = isLittleEndian ?
+			(header[4] & 0xFF) | ((header[5] & 0xFF) << 8) | ((header[6] & 0xFF) << 16) | ((header[7] & 0xFF) << 24) :
+			(header[7] & 0xFF) | ((header[6] & 0xFF) << 8) | ((header[5] & 0xFF) << 16) | ((header[4] & 0xFF) << 24);
+		file.seek(offset);
+		TIFFDirectory directory = new TIFFDirectory(file, isLittleEndian, loader);
+		ImageData image = directory.read();
+		/* A baseline reader is only expected to read the first directory */
+		images = [image];
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return images;
+}
+
+void unloadIntoByteStream(ImageLoader loader) {
+	/* We do not currently support writing multi-page tiff,
+	 * so we use the first image data in the loader's array. */
+	ImageData image = loader.data[0];
+	TIFFDirectory directory = new TIFFDirectory(image);
+	try {
+		directory.writeToStream(outputStream);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/TIFFModifiedHuffmanCodec.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.TIFFModifiedHuffmanCodec;
+
+import dwt.SWT;
+
+/*
+* Decoder for
+* - CCITT Group 3 1-Dimensional Modified Huffman run length encoding
+*   (TIFF compression type 2)
+* - CCITT T.4 bi-level encoding 1D
+*   (TIFF compression type 3 option 1D)
+*/
+final class TIFFModifiedHuffmanCodec {
+	static final short[][][] BLACK_CODE = [
+		/* 2 bits  */
+		[[ cast(short)2, 3], [ cast(short)3, 2]],
+		/* 3 bits  */
+		[[ cast(short)2, 1], [ cast(short)3, 4]],
+		/* 4 bits  */
+		[[ cast(short)2, 6], [ cast(short)3, 5]],
+		/* 5 bits  */
+		[[ cast(short)3, 7]],
+		/* 6 bits  */
+		[[ cast(short)4, 9], [ cast(short)5, 8]],
+		/* 7 bits  */
+		[[ cast(short)4, 10], [ cast(short)5, 11], [ cast(short)7, 12]],
+		/* 8 bits  */
+		[[ cast(short)4, 13], [ cast(short)7, 14]],
+		/* 9 bits  */
+		[[ cast(short)24, 15]],
+		/* 10 bits */
+		[[ cast(short)8, 18], [ cast(short)15, 64], [ cast(short)23, 16], [ cast(short)24, 17], [ cast(short)55, 0]],
+		/* 11 bits */
+		[/* EOL */[ cast(short)0, -1], [ cast(short)8, 1792], [ cast(short)23, 24], [ cast(short)24, 25], [ cast(short)40, 23], [ cast(short)55, 22], [ cast(short)103, 19],
+		[ cast(short)104, 20], [ cast(short)108, 21], [ cast(short)12, 1856], [ cast(short)13, 1920]],
+		/* 12 bits */
+		[[ cast(short)18, 1984], [ cast(short)19, 2048], [ cast(short)20, 2112], [ cast(short)21, 2176], [ cast(short)22, 2240], [ cast(short)23, 2304],
+		[ cast(short)28, 2368], [ cast(short)29, 2432], [ cast(short)30, 2496], [ cast(short)31, 2560], [ cast(short)36, 52], [ cast(short)39, 55], [ cast(short)40, 56],
+		[ cast(short)43, 59], [ cast(short)44, 60], [ cast(short)51, 320], [ cast(short)52, 384], [ cast(short)53, 448], [ cast(short)55, 53], [ cast(short)56, 54], [ cast(short)82, 50],
+		[ cast(short)83, 51], [ cast(short)84, 44], [ cast(short)85, 45], [ cast(short)86, 46], [ cast(short)87, 47], [ cast(short)88, 57], [ cast(short)89, 58], [ cast(short)90, 61],
+		[ cast(short)91, 256], [ cast(short)100, 48], [ cast(short)101, 49], [ cast(short)102, 62], [ cast(short)103, 63], [ cast(short)104, 30], [ cast(short)105, 31],
+		[ cast(short)106, 32], [ cast(short)107, 33], [ cast(short)108, 40], [ cast(short)109, 41], [ cast(short)200, 128], [ cast(short)201, 192], [ cast(short)202, 26],
+		[ cast(short)203, 27], [ cast(short)204, 28], [ cast(short)205, 29], [ cast(short)210, 34], [ cast(short)211, 35], [ cast(short)212, 36], [ cast(short)213, 37],
+		[ cast(short)214, 38], [ cast(short)215, 39], [ cast(short)218, 42], [ cast(short)219, 43]],
+		/* 13 bits */
+		[[ cast(short)74, 640], [ cast(short)75, 704], [ cast(short)76, 768], [ cast(short)77, 832], [ cast(short)82, 1280], [ cast(short)83, 1344], [ cast(short)84, 1408],
+		[ cast(short)85, 1472], [ cast(short)90, 1536], [ cast(short)91, 1600], [ cast(short)100, 1664], [ cast(short)101, 1728], [ cast(short)108, 512],
+		[ cast(short)109, 576], [ cast(short)114, 896], [ cast(short)115, 960], [ cast(short)116, 1024], [ cast(short)117, 1088], [ cast(short)118, 1152],
+		[ cast(short)119, 1216]]
+	];
+
+	static final short[][][] WHITE_CODE = [
+		/* 4 bits */
+		[[ cast(short)7, 2], [ cast(short)8, 3], [ cast(short)11, 4], [ cast(short)12, 5], [ cast(short)14, 6], [ cast(short)15, 7]],
+		/* 5 bits */
+		[[ cast(short)7, 10], [ cast(short)8, 11], [ cast(short)18, 128], [ cast(short)19, 8], [ cast(short)20, 9], [ cast(short)27, 64]],
+		/* 6 bits */
+		[[ cast(short)3, 13], [ cast(short)7, 1], [ cast(short)8, 12], [ cast(short)23, 192], [ cast(short)24, 1664], [ cast(short)42, 16], [ cast(short)43, 17], [ cast(short)52, 14],
+		[ cast(short)53, 15]],
+		/* 7 bits */
+		[[ cast(short)3, 22], [ cast(short)4, 23], [ cast(short)8, 20], [ cast(short)12, 19], [ cast(short)19, 26], [ cast(short)23, 21], [ cast(short)24, 28], [ cast(short)36, 27],
+		[ cast(short)39, 18], [ cast(short)40, 24], [ cast(short)43, 25], [ cast(short)55, 256]],
+		/* 8 bits */
+		[[ cast(short)2, 29], [ cast(short)3, 30], [ cast(short)4, 45], [ cast(short)5, 46], [ cast(short)10, 47], [ cast(short)11, 48], [ cast(short)18, 33], [ cast(short)19, 34],
+		[ cast(short)20, 35], [ cast(short)21, 36], [ cast(short)22, 37], [ cast(short)23, 38], [ cast(short)26, 31], [ cast(short)27, 32], [ cast(short)36, 53], [ cast(short)37, 54],
+		[ cast(short)40, 39], [ cast(short)41, 40], [ cast(short)42, 41], [ cast(short)43, 42], [ cast(short)44, 43], [ cast(short)45, 44], [ cast(short)50, 61], [ cast(short)51, 62],
+		[ cast(short)52, 63], [ cast(short)53, 0], [ cast(short)54, 320], [ cast(short)55, 384], [ cast(short)74, 59], [ cast(short)75, 60], [ cast(short)82, 49], [ cast(short)83, 50],
+		[ cast(short)84, 51], [ cast(short)85, 52], [ cast(short)88, 55], [ cast(short)89, 56], [ cast(short)90, 57], [ cast(short)91, 58], [ cast(short)100, 448],
+		[ cast(short)101, 512], [ cast(short)103, 640], [ cast(short)104, 576]],
+		/* 9 bits */
+		[[ cast(short)152, 1472], [ cast(short)153, 1536], [ cast(short)154, 1600], [ cast(short)155, 1728], [ cast(short)204, 704], [ cast(short)205, 768],
+		[ cast(short)210, 832], [ cast(short)211, 896], [ cast(short)212, 960], [ cast(short)213, 1024], [ cast(short)214, 1088], [ cast(short)215, 1152],
+		[ cast(short)216, 1216], [ cast(short)217, 1280], [ cast(short)218, 1344], [ cast(short)219, 1408]],
+		/* 10 bits */
+		[],
+		/* 11 bits */
+		[[ cast(short)8, 1792], [ cast(short)12, 1856], [ cast(short)13, 1920]],
+		/* 12 bits */
+		[/* EOL */[ cast(short)1, -1], [ cast(short)18, 1984], [ cast(short)19, 2048], [ cast(short)20, 2112], [ cast(short)21, 2176], [ cast(short)22, 2240], [ cast(short)23, 2304],
+		[ cast(short)28, 2368], [ cast(short)29, 2432], [ cast(short)30, 2496], [ cast(short)31, 2560]]
+	];
+
+	static final int BLACK_MIN_BITS = 2;
+	static final int WHITE_MIN_BITS = 4;
+
+	bool isWhite;
+	int whiteValue = 0;
+	int blackValue = 1;
+	byte[] src;
+	byte[] dest;
+	int byteOffsetSrc = 0;
+	int bitOffsetSrc = 0;
+	int byteOffsetDest = 0;
+	int bitOffsetDest = 0;
+	int code = 0;
+	int nbrBits = 0;
+	/* nbr of bytes per row */
+	int rowSize;
+
+public int decode(byte[] src, byte[] dest, int offsetDest, int rowSize, int nRows) {
+	this.src = src;
+	this.dest = dest;
+	this.rowSize = rowSize;
+	byteOffsetSrc = 0;
+	bitOffsetSrc = 0;
+	byteOffsetDest = offsetDest;
+	bitOffsetDest = 0;
+	int cnt = 0;
+	while (cnt < nRows && decodeRow()) {
+		cnt++;
+		/* byte aligned */
+		if (bitOffsetDest > 0) {
+			byteOffsetDest++;
+			bitOffsetDest = 0;
+		}
+	}
+	return byteOffsetDest - offsetDest;
+}
+
+bool decodeRow() {
+	isWhite = true;
+	int n = 0;
+	while (n < rowSize) {
+		int runLength = decodeRunLength();
+		if (runLength < 0) return false;
+		n += runLength;
+		setNextBits(isWhite ? whiteValue : blackValue, runLength);
+		isWhite = !isWhite;
+	}
+	return true;
+}
+
+int decodeRunLength() {
+	int runLength = 0;
+	int partialRun = 0;
+	short[][][] huffmanCode = isWhite ? WHITE_CODE : BLACK_CODE;
+	while (true) {
+		bool found = false;
+		nbrBits = isWhite ? WHITE_MIN_BITS : BLACK_MIN_BITS;
+		code = getNextBits(nbrBits);
+		for (int i = 0; i < huffmanCode.length; i++) {
+			for (int j = 0; j < huffmanCode[i].length; j++) {
+				if (huffmanCode[i][j][0] == code) {
+					found = true;
+					partialRun = huffmanCode[i][j][1];
+					if (partialRun == -1) {
+						/* Stop when reaching final EOL on last byte */
+						if (byteOffsetSrc == src.length - 1) return -1;
+						/* Group 3 starts each row with an EOL - ignore it */
+					} else {
+						runLength += partialRun;
+						if (partialRun < 64) return runLength;
+					}
+					break;
+				}
+			}
+			if (found) break;
+			code = code << 1 | getNextBit();
+		}
+		if (!found) SWT.error(SWT.ERROR_INVALID_IMAGE);
+	}
+}
+
+int getNextBit() {
+	int value = (src[byteOffsetSrc] >>> (7 - bitOffsetSrc)) & 0x1;
+	bitOffsetSrc++;
+	if (bitOffsetSrc > 7) {
+		byteOffsetSrc++;
+		bitOffsetSrc = 0;
+	}
+	return value;
+}
+
+int getNextBits(int cnt) {
+	int value = 0;
+	for (int i = 0; i < cnt; i++) {
+		value = value << 1 | getNextBit();
+	}
+	return value;
+}
+
+void setNextBits(int value, int cnt) {
+	int n = cnt;
+	while (bitOffsetDest > 0 && bitOffsetDest <= 7 && n > 0) {
+		dest[byteOffsetDest] = value == 1 ?
+			cast(byte)(dest[byteOffsetDest] | (1 << (7 - bitOffsetDest))) :
+			cast(byte)(dest[byteOffsetDest] & ~(1 << (7 - bitOffsetDest)));
+		n--;
+		bitOffsetDest++;
+	}
+	if (bitOffsetDest == 8) {
+		byteOffsetDest++;
+		bitOffsetDest = 0;
+	}
+	while (n >= 8) {
+		dest[byteOffsetDest++] = cast(byte) (value == 1 ? 0xFF : 0);
+		n -= 8;
+	}
+	while (n > 0) {
+		dest[byteOffsetDest] = value == 1 ?
+			cast(byte)(dest[byteOffsetDest] | (1 << (7 - bitOffsetDest))) :
+			cast(byte)(dest[byteOffsetDest] & ~(1 << (7 - bitOffsetDest)));
+		n--;
+		bitOffsetDest++;
+	}
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/TIFFRandomFileAccess.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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.TIFFRandomFileAccess;
+
+import dwt.internal.image.LEDataInputStream;
+import Math = tango.math.Math;
+import tango.core.Exception;
+
+final class TIFFRandomFileAccess {
+
+	LEDataInputStream inputStream;
+	int start, current, next;
+	byte[][] buffers;
+
+	static final int CHUNK_SIZE = 8192;
+	static final int LIST_SIZE = 128;
+
+public this(LEDataInputStream stream) {
+	inputStream = stream;
+	start = current = next = inputStream.getPosition();
+	buffers = new byte[][](LIST_SIZE);
+}
+
+void seek(int pos) {
+	if (pos == current) return;
+	if (pos < start) throw new IOException( "pos < start" );
+	current = pos;
+	if (current > next) {
+		int n = current - next;
+		/* store required bytes */
+		int index = next / CHUNK_SIZE;
+		int offset = next % CHUNK_SIZE;
+		while (n > 0) {
+			if (index >= buffers.length) {
+				byte[][] oldBuffers = buffers;
+				buffers = new byte[][]( Math.max(index + 1, oldBuffers.length + LIST_SIZE) );
+                buffers[ 0 .. oldBuffers.length ] = oldBuffers[];
+			}
+			if (buffers[index] == null) buffers[index] = new byte[CHUNK_SIZE];
+			int cnt = inputStream.read(buffers[index], offset, Math.min(n, CHUNK_SIZE - offset));
+			n -= cnt;
+			next += cnt;
+			index++;
+			offset = 0;
+		}
+	}
+}
+
+void read(byte b[]) {
+	int size = b.length;
+	int nCached = Math.min(size, next - current);
+	int nMissing = size - next + current;
+	int destNext = 0;
+	if (nCached > 0) {
+		/* Get cached bytes */
+		int index = current / CHUNK_SIZE;
+		int offset = current % CHUNK_SIZE;
+		while (nCached > 0) {
+			int cnt = Math.min(nCached, CHUNK_SIZE - offset);
+            b[ destNext .. destNext+cnt ] = buffers[index][ offset .. offset+cnt ];
+			nCached -= cnt;
+			destNext += cnt;
+			index++;
+			offset = 0;
+		}
+	}
+	if (nMissing > 0) {
+		/* Read required bytes */
+		int index = next / CHUNK_SIZE;
+		int offset = next % CHUNK_SIZE;
+		while (nMissing > 0) {
+			if (index >= buffers.length) {
+				byte[][] oldBuffers = buffers;
+				buffers = new byte[][](Math.max(index, oldBuffers.length + LIST_SIZE));
+                buffers[ 0 .. oldBuffers.length ] = oldBuffers[];
+			}
+			if (buffers[index] == null) buffers[index] = new byte[CHUNK_SIZE];
+			int cnt = inputStream.read(buffers[index], offset, Math.min(nMissing, CHUNK_SIZE - offset));
+            b[ destNext .. destNext+cnt ] = buffers[index][ offset .. offset+cnt ];
+			nMissing -= cnt;
+			next += cnt;
+			destNext += cnt;
+			index++;
+			offset = 0;
+		}
+	}
+	current += size;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/WinBMPFileFormat.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,699 @@
+/*******************************************************************************
+ * 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.WinBMPFileFormat;
+
+public import dwt.internal.image.FileFormat;
+public import dwt.graphics.PaletteData;
+import dwt.graphics.Point;
+import dwt.graphics.RGB;
+import dwt.dwthelper.ByteArrayOutputStream;
+import dwt.SWT;
+
+import tango.core.Exception;
+
+final class WinBMPFileFormat : FileFormat {
+
+	static final int BMPFileHeaderSize = 14;
+	static final int BMPHeaderFixedSize = 40;
+	int importantColors;
+    Point pelsPerMeter;
+
+public this(){
+    pelsPerMeter = new Point(0, 0);
+}
+
+/**
+ * Compress numBytes bytes of image data from src, storing in dest
+ * (starting at 0), using the technique specified by comp.
+ * If last is true, this indicates the last line of the image.
+ * Answer the size of the compressed data.
+ */
+int compress(int comp, byte[] src, int srcOffset, int numBytes, byte[] dest, bool last) {
+	if (comp == 1) { // BMP_RLE8_COMPRESSION
+		return compressRLE8Data(src, srcOffset, numBytes, dest, last);
+	}
+	if (comp == 2) { // BMP_RLE4_COMPRESSION
+		return compressRLE4Data(src, srcOffset, numBytes, dest, last);
+	}
+	SWT.error(SWT.ERROR_INVALID_IMAGE);
+	return 0;
+}
+int compressRLE4Data(byte[] src, int srcOffset, int numBytes, byte[] dest, bool last) {
+	int sp = srcOffset, end = srcOffset + numBytes, dp = 0;
+	int size = 0, left, i, n;
+	byte theByte;
+	while (sp < end) {
+		/* find two consecutive bytes that are the same in the next 128 */
+		left = end - sp - 1;
+		if (left > 127)
+			left = 127;
+		for (n = 0; n < left; n++) {
+			if (src[sp + n] == src[sp + n + 1])
+				break;
+		}
+		/* if there is only one more byte in the scan line, include it */
+		if (n < 127 && n == left)
+			n++;
+		/* store the intervening data */
+		switch (n) {
+			case 0:
+				break;
+			case 1: /* handled separately because 0,2 is a command */
+				dest[dp] = 2; dp++; /* 1 byte == 2 pixels */
+				dest[dp] = src[sp];
+				dp++; sp++;
+				size += 2;
+				break;
+			default:
+				dest[dp] = 0; dp++;
+				dest[dp] = cast(byte)(n + n); dp++; /* n bytes = n*2 pixels */
+				for (i = n; i > 0; i--) {
+					dest[dp] = src[sp];
+					dp++; sp++;
+				}
+				size += 2 + n;
+				if ((n & 1) != 0) { /* pad to word */
+					dest[dp] = 0;
+					dp++;
+					size++;
+				}
+				break;
+		}
+		/* find the length of the next run (up to 127) and store it */
+		left = end - sp;
+		if (left > 0) {
+			if (left > 127)
+				left = 127;
+			theByte = src[sp];
+			for (n = 1; n < left; n++) {
+				if (src[sp + n] != theByte)
+					break;
+			}
+			dest[dp] = cast(byte)(n + n); dp++; /* n bytes = n*2 pixels */
+			dest[dp] = theByte; dp++;
+			sp += n;
+			size += 2;
+		}
+	}
+
+	/* store the end of line or end of bitmap codes */
+	dest[dp] = 0; dp++;
+	if (last) {
+		dest[dp] = 1; dp++;
+	} else {
+		dest[dp] = 0; dp++;
+	}
+	size += 2;
+
+	return size;
+}
+int compressRLE8Data(byte[] src, int srcOffset, int numBytes, byte[] dest, bool last) {
+	int sp = srcOffset, end = srcOffset + numBytes, dp = 0;
+	int size = 0, left, i, n;
+	byte theByte;
+	while (sp < end) {
+		/* find two consecutive bytes that are the same in the next 256 */
+		left = end - sp - 1;
+		if (left > 254)
+			left = 254;
+		for (n = 0; n < left; n++) {
+			if (src[sp + n] == src[sp + n + 1])
+				break;
+		}
+		/* if there is only one more byte in the scan line, include it */
+		if (n == left)
+			n++;
+		/* store the intervening data */
+		switch (n) {
+			case 0:
+				break;
+			case 2: /* handled separately because 0,2 is a command */
+				dest[dp] = 1; dp++;
+				dest[dp] = src[sp];
+				dp++; sp++;
+				size += 2;
+				/* don't break, fall through */
+			case 1: /* handled separately because 0,1 is a command */
+				dest[dp] = 1; dp++;
+				dest[dp] = src[sp];
+				dp++; sp++;
+				size += 2;
+				break;
+			default:
+				dest[dp] = 0; dp++;
+				dest[dp] = cast(byte)n; dp++;
+				for (i = n; i > 0; i--) {
+					dest[dp] = src[sp];
+					dp++; sp++;
+				}
+				size += 2 + n;
+				if ((n & 1) != 0) { /* pad to word */
+					dest[dp] = 0;
+					dp++;
+					size++;
+				}
+				break;
+		}
+		/* find the length of the next run (up to 255) and store it */
+		left = end - sp;
+		if (left > 0) {
+			if (left > 255)
+				left = 255;
+			theByte = src[sp];
+			for (n = 1; n < left; n++) {
+				if (src[sp + n] != theByte)
+					break;
+			}
+			dest[dp] = cast(byte)n; dp++;
+			dest[dp] = theByte; dp++;
+			sp += n;
+			size += 2;
+		}
+	}
+
+	/* store the end of line or end of bitmap codes */
+	dest[dp] = 0; dp++;
+	if (last) {
+		dest[dp] = 1; dp++;
+	} else {
+		dest[dp] = 0; dp++;
+	}
+	size += 2;
+
+	return size;
+}
+void decompressData(byte[] src, byte[] dest, int stride, int cmp) {
+	if (cmp == 1) { // BMP_RLE8_COMPRESSION
+		if (decompressRLE8Data(src, src.length, stride, dest, dest.length) <= 0)
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		return;
+	}
+	if (cmp == 2) { // BMP_RLE4_COMPRESSION
+		if (decompressRLE4Data(src, src.length, stride, dest, dest.length) <= 0)
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		return;
+	}
+	SWT.error(SWT.ERROR_INVALID_IMAGE);
+}
+int decompressRLE4Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) {
+	int sp = 0;
+	int se = numBytes;
+	int dp = 0;
+	int de = destSize;
+	int x = 0, y = 0;
+	while (sp < se) {
+		int len = src[sp] & 0xFF;
+		sp++;
+		if (len == 0) {
+			len = src[sp] & 0xFF;
+			sp++;
+			switch (len) {
+				case 0: /* end of line */
+					y++;
+					x = 0;
+					dp = y * stride;
+					if (dp > de)
+						return -1;
+					break;
+				case 1: /* end of bitmap */
+					return 1;
+				case 2: /* delta */
+					x += src[sp] & 0xFF;
+					sp++;
+					y += src[sp] & 0xFF;
+					sp++;
+					dp = y * stride + x / 2;
+					if (dp > de)
+						return -1;
+					break;
+				default: /* absolute mode run */
+					if ((len & 1) != 0) /* odd run lengths not currently supported */
+						return -1;
+					x += len;
+					len = len / 2;
+					if (len > (se - sp))
+						return -1;
+					if (len > (de - dp))
+						return -1;
+					for (int i = 0; i < len; i++) {
+						dest[dp] = src[sp];
+						dp++;
+						sp++;
+					}
+					if ((sp & 1) != 0)
+						sp++; /* word align sp? */
+					break;
+			}
+		} else {
+			if ((len & 1) != 0)
+				return -1;
+			x += len;
+			len = len / 2;
+			byte theByte = src[sp];
+			sp++;
+			if (len > (de - dp))
+				return -1;
+			for (int i = 0; i < len; i++) {
+				dest[dp] = theByte;
+				dp++;
+			}
+		}
+	}
+	return 1;
+}
+int decompressRLE8Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) {
+	int sp = 0;
+	int se = numBytes;
+	int dp = 0;
+	int de = destSize;
+	int x = 0, y = 0;
+	while (sp < se) {
+		int len = src[sp] & 0xFF;
+		sp++;
+		if (len == 0) {
+			len = src[sp] & 0xFF;
+			sp++;
+			switch (len) {
+				case 0: /* end of line */
+					y++;
+					x = 0;
+					dp = y * stride;
+					if (dp > de)
+						return -1;
+					break;
+				case 1: /* end of bitmap */
+					return 1;
+				case 2: /* delta */
+					x += src[sp] & 0xFF;
+					sp++;
+					y += src[sp] & 0xFF;
+					sp++;
+					dp = y * stride + x;
+					if (dp > de)
+						return -1;
+					break;
+				default: /* absolute mode run */
+					if (len > (se - sp))
+						return -1;
+					if (len > (de - dp))
+						return -1;
+					for (int i = 0; i < len; i++) {
+						dest[dp] = src[sp];
+						dp++;
+						sp++;
+					}
+					if ((sp & 1) != 0)
+						sp++; /* word align sp? */
+					x += len;
+					break;
+			}
+		} else {
+			byte theByte = src[sp];
+			sp++;
+			if (len > (de - dp))
+				return -1;
+			for (int i = 0; i < len; i++) {
+				dest[dp] = theByte;
+				dp++;
+			}
+			x += len;
+		}
+	}
+	return 1;
+}
+bool isFileFormat(LEDataInputStream stream) {
+	try {
+		byte[] header = new byte[18];
+		stream.read(header);
+		stream.unread(header);
+		int infoHeaderSize = (header[14] & 0xFF) | ((header[15] & 0xFF) << 8) | ((header[16] & 0xFF) << 16) | ((header[17] & 0xFF) << 24);
+		return header[0] == 0x42 && header[1] == 0x4D && infoHeaderSize >= BMPHeaderFixedSize;
+	} catch (TracedException e) {
+		return false;
+	}
+}
+byte[] loadData(byte[] infoHeader) {
+	int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
+	int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
+	int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
+	int stride = (width * bitCount + 7) / 8;
+	stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
+	byte[] data = loadData(infoHeader, stride);
+	flipScanLines(data, stride, height);
+	return data;
+}
+byte[] loadData(byte[] infoHeader, int stride) {
+	int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
+	if (height < 0) height = -height;
+	int dataSize = height * stride;
+	byte[] data = new byte[dataSize];
+	int cmp = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24);
+	if (cmp == 0 || cmp == 3) { // BMP_NO_COMPRESSION
+		try {
+			if (inputStream.read(data) != dataSize)
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	} else {
+		int compressedSize = (infoHeader[20] & 0xFF) | ((infoHeader[21] & 0xFF) << 8) | ((infoHeader[22] & 0xFF) << 16) | ((infoHeader[23] & 0xFF) << 24);
+		byte[] compressed = new byte[compressedSize];
+		try {
+			if (inputStream.read(compressed) != compressedSize)
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		decompressData(compressed, data, stride, cmp);
+	}
+	return data;
+}
+int[] loadFileHeader() {
+	int[] header = new int[5];
+	try {
+		header[0] = inputStream.readShort();
+		header[1] = inputStream.readInt();
+		header[2] = inputStream.readShort();
+		header[3] = inputStream.readShort();
+		header[4] = inputStream.readInt();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	if (header[0] != 0x4D42)
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	return header;
+}
+ImageData[] loadFromByteStream() {
+	int[] fileHeader = loadFileHeader();
+	byte[] infoHeader = new byte[BMPHeaderFixedSize];
+	try {
+		inputStream.read(infoHeader);
+	} catch (TracedException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
+	int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
+	if (height < 0) height = -height;
+	int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
+	this.compression = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24);
+	PaletteData palette = loadPalette(infoHeader);
+	if (inputStream.getPosition() < fileHeader[4]) {
+		// Seek to the specified offset
+		try {
+			inputStream.skip(fileHeader[4] - inputStream.getPosition());
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+	byte[] data = loadData(infoHeader);
+	this.importantColors = (infoHeader[36] & 0xFF) | ((infoHeader[37] & 0xFF) << 8) | ((infoHeader[38] & 0xFF) << 16) | ((infoHeader[39] & 0xFF) << 24);
+	int xPelsPerMeter = (infoHeader[24] & 0xFF) | ((infoHeader[25] & 0xFF) << 8) | ((infoHeader[26] & 0xFF) << 16) | ((infoHeader[27] & 0xFF) << 24);
+	int yPelsPerMeter = (infoHeader[28] & 0xFF) | ((infoHeader[29] & 0xFF) << 8) | ((infoHeader[30] & 0xFF) << 16) | ((infoHeader[31] & 0xFF) << 24);
+	this.pelsPerMeter = new Point(xPelsPerMeter, yPelsPerMeter);
+	int type = (this.compression == 1 /*BMP_RLE8_COMPRESSION*/) || (this.compression == 2 /*BMP_RLE4_COMPRESSION*/) ? SWT.IMAGE_BMP_RLE : SWT.IMAGE_BMP;
+	return [
+		ImageData.internal_new(
+			width,
+			height,
+			bitCount,
+			palette,
+			4,
+			data,
+			0,
+			null,
+			null,
+			-1,
+			-1,
+			type,
+			0,
+			0,
+			0,
+			0)
+	];
+}
+PaletteData loadPalette(byte[] infoHeader) {
+	int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
+	if (depth <= 8) {
+		int numColors = (infoHeader[32] & 0xFF) | ((infoHeader[33] & 0xFF) << 8) | ((infoHeader[34] & 0xFF) << 16) | ((infoHeader[35] & 0xFF) << 24);
+		if (numColors == 0) {
+			numColors = 1 << depth;
+		} else {
+			if (numColors > 256)
+				numColors = 256;
+		}
+		byte[] buf = new byte[numColors * 4];
+		try {
+			if (inputStream.read(buf) != buf.length)
+				SWT.error(SWT.ERROR_INVALID_IMAGE);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+		return paletteFromBytes(buf, numColors);
+	}
+	if (depth == 16) {
+		if (this.compression == 3) {
+			try {
+				return new PaletteData(inputStream.readInt(), inputStream.readInt(), inputStream.readInt());
+			} catch (IOException e) {
+				SWT.error(SWT.ERROR_IO, e);
+			}
+		}
+		return new PaletteData(0x7C00, 0x3E0, 0x1F);
+	}
+	if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
+	if (this.compression == 3) {
+		try {
+			return new PaletteData(inputStream.readInt(), inputStream.readInt(), inputStream.readInt());
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+	return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
+}
+PaletteData paletteFromBytes(byte[] bytes, int numColors) {
+	int bytesOffset = 0;
+	RGB[] colors = new RGB[numColors];
+	for (int i = 0; i < numColors; i++) {
+		colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
+			bytes[bytesOffset + 1] & 0xFF,
+			bytes[bytesOffset] & 0xFF);
+		bytesOffset += 4;
+	}
+	return new PaletteData(colors);
+}
+/**
+ * Answer a byte array containing the BMP representation of
+ * the given device independent palette.
+ */
+static byte[] paletteToBytes(PaletteData pal) {
+	int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256);
+	byte[] bytes = new byte[n * 4];
+	int offset = 0;
+	for (int i = 0; i < n; i++) {
+		RGB col = pal.colors[i];
+		bytes[offset] = cast(byte)col.blue;
+		bytes[offset + 1] = cast(byte)col.green;
+		bytes[offset + 2] = cast(byte)col.red;
+		offset += 4;
+	}
+	return bytes;
+}
+/**
+ * Unload the given image's data into the given byte stream
+ * using the given compression strategy.
+ * Answer the number of bytes written.
+ */
+int unloadData(ImageData image, OutputStream ostr, int comp) {
+	int totalSize = 0;
+	try {
+		if (comp == 0)
+			return unloadDataNoCompression(image, ostr);
+		int bpl = (image.width * image.depth + 7) / 8;
+		int bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
+		int imageBpl = image.bytesPerLine;
+		// Compression can actually take twice as much space, in worst case
+		byte[] buf = new byte[bmpBpl * 2];
+		int srcOffset = imageBpl * (image.height - 1); // Start at last line
+		byte[] data = image.data;
+		totalSize = 0;
+		byte[] buf2 = new byte[32768];
+		int buf2Offset = 0;
+		for (int y = image.height - 1; y >= 0; y--) {
+			int lineSize = compress(comp, data, srcOffset, bpl, buf, y == 0);
+			if (buf2Offset + lineSize > buf2.length) {
+				ostr.write(buf2, 0, buf2Offset);
+				buf2Offset = 0;
+			}
+            buf2[ buf2Offset .. buf2Offset+lineSize ] = buf[ 0 .. lineSize ];
+			buf2Offset += lineSize;
+			totalSize += lineSize;
+			srcOffset -= imageBpl;
+		}
+		if (buf2Offset > 0)
+			ostr.write(buf2, 0, buf2Offset);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return totalSize;
+}
+/**
+ * Prepare the given image's data for unloading into a byte stream
+ * using no compression strategy.
+ * Answer the number of bytes written.
+ */
+int unloadDataNoCompression(ImageData image, OutputStream ostr) {
+	int bmpBpl = 0;
+	try {
+		int bpl = (image.width * image.depth + 7) / 8;
+		bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
+		int linesPerBuf = 32678 / bmpBpl;
+		byte[] buf = new byte[linesPerBuf * bmpBpl];
+		byte[] data = image.data;
+		int imageBpl = image.bytesPerLine;
+		int dataIndex = imageBpl * (image.height - 1); // Start at last line
+		if (image.depth == 16) {
+			for (int y = 0; y < image.height; y += linesPerBuf) {
+				int count = image.height - y;
+				if (linesPerBuf < count) count = linesPerBuf;
+				int bufOffset = 0;
+				for (int i = 0; i < count; i++) {
+					for (int wIndex = 0; wIndex < bpl; wIndex += 2) {
+						buf[bufOffset + wIndex + 1] = data[dataIndex + wIndex + 1];
+						buf[bufOffset + wIndex] = data[dataIndex + wIndex];
+					}
+					bufOffset += bmpBpl;
+					dataIndex -= imageBpl;
+				}
+				ostr.write(buf, 0, bufOffset);
+			}
+		} else {
+			for (int y = 0; y < image.height; y += linesPerBuf) {
+				int tmp = image.height - y;
+				int count = tmp < linesPerBuf ? tmp : linesPerBuf;
+				int bufOffset = 0;
+				for (int i = 0; i < count; i++) {
+                    buf[ bufOffset .. bufOffset+bpl ] = data[ dataIndex .. dataIndex+bpl ];
+					bufOffset += bmpBpl;
+					dataIndex -= imageBpl;
+				}
+				ostr.write(buf, 0, bufOffset);
+			}
+		}
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return bmpBpl * image.height;
+}
+/**
+ * Unload a DeviceIndependentImage using Windows .BMP format into the given
+ * byte stream.
+ */
+void unloadIntoByteStream(ImageLoader loader) {
+	ImageData image = loader.data[0];
+	byte[] rgbs;
+	int numCols;
+	if (!((image.depth == 1) || (image.depth == 4) || (image.depth == 8) ||
+		  (image.depth == 16) || (image.depth == 24) || (image.depth == 32)))
+			SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
+	int comp = this.compression;
+	if (!((comp == 0) || ((comp == 1) && (image.depth == 8)) ||
+		  ((comp == 2) && (image.depth == 4))))
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+	PaletteData pal = image.palette;
+	if ((image.depth == 16) || (image.depth == 24) || (image.depth == 32)) {
+		if (!pal.isDirect)
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		numCols = 0;
+		rgbs = null;
+	} else {
+		if (pal.isDirect)
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+		numCols = pal.colors.length;
+		rgbs = paletteToBytes(pal);
+	}
+	// Fill in file header, except for bfsize, which is done later.
+	int headersSize = BMPFileHeaderSize + BMPHeaderFixedSize;
+	int[] fileHeader = new int[5];
+	fileHeader[0] = 0x4D42;	// Signature
+	fileHeader[1] = 0; // File size - filled in later
+	fileHeader[2] = 0; // Reserved 1
+	fileHeader[3] = 0; // Reserved 2
+	fileHeader[4] = headersSize; // Offset to data
+	if (rgbs != null) {
+		fileHeader[4] += rgbs.length;
+	}
+
+	// Prepare data. This is done first so we don't have to try to rewind
+	// the stream and fill in the details later.
+	ByteArrayOutputStream ostr = new ByteArrayOutputStream();
+	unloadData(image, ostr, comp);
+	byte[] data = ostr.toByteArray();
+
+	// Calculate file size
+	fileHeader[1] = fileHeader[4] + data.length;
+
+	// Write the headers
+	try {
+		outputStream.writeShort(fileHeader[0]);
+		outputStream.writeInt(fileHeader[1]);
+		outputStream.writeShort(fileHeader[2]);
+		outputStream.writeShort(fileHeader[3]);
+		outputStream.writeInt(fileHeader[4]);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	try {
+		outputStream.writeInt(BMPHeaderFixedSize);
+		outputStream.writeInt(image.width);
+		outputStream.writeInt(image.height);
+		outputStream.writeShort(1);
+		outputStream.writeShort(cast(short)image.depth);
+		outputStream.writeInt(comp);
+		outputStream.writeInt(data.length);
+		outputStream.writeInt(pelsPerMeter.x);
+		outputStream.writeInt(pelsPerMeter.y);
+		outputStream.writeInt(numCols);
+		outputStream.writeInt(importantColors);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+
+	// Unload palette
+	if (numCols > 0) {
+		try {
+			outputStream.write(rgbs);
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+		}
+	}
+
+	// Unload the data
+	try {
+		outputStream.write(data);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+void flipScanLines(byte[] data, int stride, int height) {
+	int i1 = 0;
+	int i2 = (height - 1) * stride;
+	for (int i = 0; i < height / 2; i++) {
+		for (int index = 0; index < stride; index++) {
+			byte b = data[index + i1];
+			data[index + i1] = data[index + i2];
+			data[index + i2] = b;
+		}
+		i1 += stride;
+		i2 -= stride;
+	}
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/WinICOFileFormat.d	Sun Jan 06 22:54:14 2008 +0100
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * 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.WinICOFileFormat;
+
+public import dwt.internal.image.FileFormat;
+public import dwt.graphics.PaletteData;
+import dwt.internal.image.WinBMPFileFormat;
+import dwt.SWT;
+
+import tango.core.Exception;
+
+final class WinICOFileFormat : FileFormat {
+
+byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
+	// Destructively bit invert data in the given byte array.
+	for (int i = startIndex; i < endIndex; i++) {
+		data[i] = cast(byte)(255 - data[i - startIndex]);
+	}
+	return data;
+}
+
+static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
+	if (pad == newPad) return data;
+	int stride = (width * depth + 7) / 8;
+	int bpl = (stride + (pad - 1)) / pad * pad;
+	int newBpl = (stride + (newPad - 1)) / newPad * newPad;
+	byte[] newData = new byte[height * newBpl];
+	int srcIndex = 0, destIndex = 0;
+	for (int y = 0; y < height; y++) {
+        newData[ destIndex .. destIndex+newBpl ] = data[ srcIndex .. srcIndex+newBpl ];
+		srcIndex += bpl;
+		destIndex += newBpl;
+	}
+	return newData;
+}
+/**
+ * Answer the size in bytes of the file representation of the given
+ * icon
+ */
+int iconSize(ImageData i) {
+	int shapeDataStride = (i.width * i.depth + 31) / 32 * 4;
+	int maskDataStride = (i.width + 31) / 32 * 4;
+	int dataSize = (shapeDataStride + maskDataStride) * i.height;
+	int paletteSize = i.palette.colors != null ? i.palette.colors.length * 4 : 0;
+	return WinBMPFileFormat.BMPHeaderFixedSize + paletteSize + dataSize;
+}
+bool isFileFormat(LEDataInputStream stream) {
+	try {
+		byte[] header = new byte[4];
+		stream.read(header);
+		stream.unread(header);
+		return header[0] == 0 && header[1] == 0 && header[2] == 1 && header[3] == 0;
+	} catch (Exception e) {
+		return false;
+	}
+}
+bool isValidIcon(ImageData i) {
+	switch (i.depth) {
+		case 1:
+		case 4:
+		case 8:
+			if (i.palette.isDirect) return false;
+			int size = i.palette.colors.length;
+			return size == 2 || size == 16 || size == 32 || size == 256;
+		case 24:
+		case 32:
+			return i.palette.isDirect;
+	}
+	return false;
+}
+int loadFileHeader(LEDataInputStream byteStream) {
+	int[] fileHeader = new int[3];
+	try {
+		fileHeader[0] = byteStream.readShort();
+		fileHeader[1] = byteStream.readShort();
+		fileHeader[2] = byteStream.readShort();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	int numIcons = fileHeader[2];
+	if (numIcons <= 0)
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	return numIcons;
+}
+int loadFileHeader(LEDataInputStream byteStream, bool hasHeader) {
+	int[] fileHeader = new int[3];
+	try {
+		if (hasHeader) {
+			fileHeader[0] = byteStream.readShort();
+			fileHeader[1] = byteStream.readShort();
+		} else {
+			fileHeader[0] = 0;
+			fileHeader[1] = 1;
+		}
+		fileHeader[2] = byteStream.readShort();
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	int numIcons = fileHeader[2];
+	if (numIcons <= 0)
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	return numIcons;
+}
+ImageData[] loadFromByteStream() {
+	int numIcons = loadFileHeader(inputStream);
+	int[][] headers = loadIconHeaders(numIcons);
+	ImageData[] icons = new ImageData[headers.length];
+	for (int i = 0; i < icons.length; i++) {
+		icons[i] = loadIcon(headers[i]);
+	}
+	return icons;
+}
+/**
+ * Load one icon from the byte stream.
+ */
+ImageData loadIcon(int[] iconHeader) {
+	byte[] infoHeader = loadInfoHeader(iconHeader);
+	WinBMPFileFormat bmpFormat = new WinBMPFileFormat();
+	bmpFormat.inputStream = inputStream;
+	PaletteData palette = bmpFormat.loadPalette(infoHeader);
+	byte[] shapeData = bmpFormat.loadData(infoHeader);
+	int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
+	int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
+	if (height < 0) height = -height;
+	int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
+	infoHeader[14] = 1;
+	infoHeader[15] = 0;
+	byte[] maskData = bmpFormat.loadData(infoHeader);
+	maskData = convertPad(maskData, width, height, 1, 4, 2);
+	bitInvertData(maskData, 0, maskData.length);
+	return ImageData.internal_new(
+		width,
+		height,
+		depth,
+		palette,
+		4,
+		shapeData,
+		2,
+		maskData,
+		null,
+		-1,
+		-1,
+		SWT.IMAGE_ICO,
+		0,
+		0,
+		0,
+		0);
+}
+int[][] loadIconHeaders(int numIcons) {
+	int[][] headers = new int[][]( numIcons, 7 );
+	try {
+		for (int i = 0; i < numIcons; i++) {
+			headers[i][0] = inputStream.read();
+			headers[i][1] = inputStream.read();
+			headers[i][2] = inputStream.readShort();
+			headers[i][3] = inputStream.readShort();
+			headers[i][4] = inputStream.readShort();
+			headers[i][5] = inputStream.readInt();
+			headers[i][6] = inputStream.readInt();
+		}
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	return headers;
+}
+byte[] loadInfoHeader(int[] iconHeader) {
+	int width = iconHeader[0];
+	int height = iconHeader[1];
+	int numColors = iconHeader[2]; // the number of colors is in the low byte, but the high byte must be 0
+	if (numColors == 0) numColors = 256; // this is specified: '00' represents '256' (0x100) colors
+	if ((numColors != 2) && (numColors != 8) && (numColors != 16) &&
+		(numColors != 32) && (numColors != 256))
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	if (inputStream.getPosition() < iconHeader[6]) {
+		// Seek to the specified offset
+		try {
+			inputStream.skip(iconHeader[6] - inputStream.getPosition());
+		} catch (IOException e) {
+			SWT.error(SWT.ERROR_IO, e);
+			return null;
+		}
+	}
+	byte[] infoHeader = new byte[WinBMPFileFormat.BMPHeaderFixedSize];
+	try {
+		inputStream.read(infoHeader);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	if (((infoHeader[12] & 0xFF) | ((infoHeader[13] & 0xFF) << 8)) != 1)
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	int infoWidth = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24);
+	int infoHeight = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24);
+	int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8);
+	if (height == infoHeight && bitCount == 1) height /= 2;
+	if (!((width == infoWidth) && (height * 2 == infoHeight) &&
+		(bitCount == 1 || bitCount == 4 || bitCount == 8 || bitCount == 24 || bitCount == 32)))
+			SWT.error(SWT.ERROR_INVALID_IMAGE);
+	infoHeader[8] = cast(byte)(height & 0xFF);
+	infoHeader[9] = cast(byte)((height >> 8) & 0xFF);
+	infoHeader[10] = cast(byte)((height >> 16) & 0xFF);
+	infoHeader[11] = cast(byte)((height >> 24) & 0xFF);
+	return infoHeader;
+}
+/**
+ * Unload a single icon
+ */
+void unloadIcon(ImageData icon) {
+	int sizeImage = (((icon.width * icon.depth + 31) / 32 * 4) +
+		((icon.width + 31) / 32 * 4)) * icon.height;
+	try {
+		outputStream.writeInt(WinBMPFileFormat.BMPHeaderFixedSize);
+		outputStream.writeInt(icon.width);
+		outputStream.writeInt(icon.height * 2);
+		outputStream.writeShort(1);
+		outputStream.writeShort(cast(short)icon.depth);
+		outputStream.writeInt(0);
+		outputStream.writeInt(sizeImage);
+		outputStream.writeInt(0);
+		outputStream.writeInt(0);
+		outputStream.writeInt(icon.palette.colors != null ? icon.palette.colors.length : 0);
+		outputStream.writeInt(0);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+
+	byte[] rgbs = WinBMPFileFormat.paletteToBytes(icon.palette);
+	try {
+		outputStream.write(rgbs);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	unloadShapeData(icon);
+	unloadMaskData(icon);
+}
+/**
+ * Unload the icon header for the given icon, calculating the offset.
+ */
+void unloadIconHeader(ImageData i) {
+	int headerSize = 16;
+	int offset = headerSize + 6;
+	int iconSize = iconSize(i);
+	try {
+		outputStream.write(i.width);
+		outputStream.write(i.height);
+		outputStream.writeShort(i.palette.colors != null ? i.palette.colors.length : 0);
+		outputStream.writeShort(0);
+		outputStream.writeShort(0);
+		outputStream.writeInt(iconSize);
+		outputStream.writeInt(offset);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+void unloadIntoByteStream(ImageLoader loader) {
+	/* We do not currently support writing multi-image ico,
+	 * so we use the first image data in the loader's array. */
+	ImageData image = loader.data[0];
+	if (!isValidIcon(image))
+		SWT.error(SWT.ERROR_INVALID_IMAGE);
+	try {
+		outputStream.writeShort(0);
+		outputStream.writeShort(1);
+		outputStream.writeShort(1);
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+	unloadIconHeader(image);
+	unloadIcon(image);
+}
+/**
+ * Unload the mask data for an icon. The data is flipped vertically
+ * and inverted.
+ */
+void unloadMaskData(ImageData icon) {
+	ImageData mask = icon.getTransparencyMask();
+	int bpl = (icon.width + 7) / 8;
+	int pad = mask.scanlinePad;
+	int srcBpl = (bpl + pad - 1) / pad * pad;
+	int destBpl = (bpl + 3) / 4 * 4;
+	byte[] buf = new byte[destBpl];
+	int offset = (icon.height - 1) * srcBpl;
+	byte[] data = mask.data;
+	try {
+		for (int i = 0; i < icon.height; i++) {
+            buf[ 0 .. bpl ] = data[ offset .. offset+bpl ];
+			bitInvertData(buf, 0, bpl);
+			outputStream.write(buf, 0, destBpl);
+			offset -= srcBpl;
+		}
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+/**
+ * Unload the shape data for an icon. The data is flipped vertically.
+ */
+void unloadShapeData(ImageData icon) {
+	int bpl = (icon.width * icon.depth + 7) / 8;
+	int pad = icon.scanlinePad;
+	int srcBpl = (bpl + pad - 1) / pad * pad;
+	int destBpl = (bpl + 3) / 4 * 4;
+	byte[] buf = new byte[destBpl];
+	int offset = (icon.height - 1) * srcBpl;
+	byte[] data = icon.data;
+	try {
+		for (int i = 0; i < icon.height; i++) {
+            buf[ 0 .. bpl ] = data[ offset .. offset+bpl ];
+			outputStream.write(buf, 0, destBpl);
+			offset -= srcBpl;
+		}
+	} catch (IOException e) {
+		SWT.error(SWT.ERROR_IO, e);
+	}
+}
+}