changeset 22:5f2e72114476

Image in work, this revision does not compile
author Frank Benoit <benoit@tionex.de>
date Sat, 26 Jan 2008 19:05:32 +0100
parents d2e87572b721
children f5482da87ed8
files dwt/dwthelper/utils.d dwt/graphics/GC.d dwt/graphics/Image.d dwt/graphics/ImageData.d dwt/graphics/ImageDataLoader.d dwt/graphics/ImageLoader.d dwt/graphics/ImageLoaderEvent.d dwt/graphics/ImageLoaderListener.d dwt/internal/gdip/Gdip.d
diffstat 9 files changed, 11262 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/dwt/dwthelper/utils.d	Sat Jan 26 16:45:05 2008 +0100
+++ b/dwt/dwthelper/utils.d	Sat Jan 26 19:05:32 2008 +0100
@@ -1,4 +1,4 @@
-/**
+/**
  * Authors: Frank Benoit <keinfarbton@googlemail.com>
  */
 module dwt.dwthelper.utils;
@@ -88,9 +88,24 @@
     return str[ pos ];
 }
 
+public void getChars( char[] src, int srcBegin, int srcEnd, char[] dst, int dstBegin){
+    dst[ dstBegin .. dstBegin + srcEnd - srcBegin ] = str[ srcBegin .. srcEnd ];
+}
+
+public bool endsWith( char[] src, char[] pattern ){
+    if( src.length < pattern.length ){
+        return false;
+    }
+    return src[ $-pattern.length .. $ ] == pattern;
+}
+
+public char[] toLowerCase( char[] src ){
+    return tango.text.Unicode.toLower( src );
+}
+
 static char[] toHex(uint value, bool prefix = true, int radix = 8){
-    return tango.text.convert.Integer.toString( 
-            value, 
+    return tango.text.convert.Integer.toString(
+            value,
             radix is 10 ? tango.text.convert.Integer.Style.Signed :
             radix is  8 ? tango.text.convert.Integer.Style.Octal  :
             radix is 16 ? tango.text.convert.Integer.Style.Hex    :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/GC.d	Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,4836 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.graphics.GC;
+
+//PORTING_TYPE
+class GC{
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.DWTException;
+import dwt.internal.Compatibility;
+import dwt.internal.gdip.Gdip;
+import dwt.internal.gdip.PointF;
+import dwt.internal.gdip.Rect;
+import dwt.internal.gdip.RectF;
+import dwt.internal.win32.BITMAP;
+import dwt.internal.win32.BITMAPINFOHEADER;
+import dwt.internal.win32.BLENDFUNCTION;
+import dwt.internal.win32.GRADIENT_RECT;
+import dwt.internal.win32.ICONINFO;
+import dwt.internal.win32.LOGBRUSH;
+import dwt.internal.win32.LOGFONT;
+import dwt.internal.win32.LOGFONTA;
+import dwt.internal.win32.LOGFONTW;
+import dwt.internal.win32.LOGPEN;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SIZE;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.TRIVERTEX;
+
+/**
+ * Class <code>GC</code> is where all of the drawing capabilities that are
+ * supported by DWT are located. Instances are used to draw on either an
+ * <code>Image</code>, a <code>Control</code>, or directly on a <code>Display</code>.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
+ * </dl>
+ *
+ * <p>
+ * The DWT drawing coordinate system is the two-dimensional space with the origin
+ * (0,0) at the top left corner of the drawing area and with (x,y) values increasing
+ * to the right and downward respectively.
+ * </p>
+ *
+ * <p>
+ * Application code must explicitly invoke the <code>GC.dispose()</code>
+ * method to release the operating system resources managed by each instance
+ * when those instances are no longer required. This is <em>particularly</em>
+ * important on Windows95 and Windows98 where the operating system has a limited
+ * number of device contexts available.
+ * </p>
+ *
+ * <p>
+ * Note: Only one of LEFT_TO_RIGHT and RIGHT_TO_LEFT may be specified.
+ * </p>
+ *
+ * @see dwt.events.PaintEvent
+ */
+
+public final class GC extends Resource {
+
+    /**
+     * the handle to the OS device context
+     * (Warning: This field is platform dependent)
+     * <p>
+     * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
+     * public API. It is marked public only so that it can be shared
+     * within the packages provided by DWT. It is not available on all
+     * platforms and should never be accessed from application code.
+     * </p>
+     */
+    public int handle;
+
+    Drawable drawable;
+    GCData data;
+
+    static final int FOREGROUND = 1 << 0;
+    static final int BACKGROUND = 1 << 1;
+    static final int FONT = 1 << 2;
+    static final int LINE_STYLE = 1 << 3;
+    static final int LINE_WIDTH = 1 << 4;
+    static final int LINE_CAP = 1 << 5;
+    static final int LINE_JOIN = 1 << 6;
+    static final int LINE_MITERLIMIT = 1 << 7;
+    static final int FOREGROUND_TEXT = 1 << 8;
+    static final int BACKGROUND_TEXT = 1 << 9;
+    static final int BRUSH = 1 << 10;
+    static final int PEN = 1 << 11;
+    static final int NULL_BRUSH = 1 << 12;
+    static final int NULL_PEN = 1 << 13;
+    static final int DRAW_OFFSET = 1 << 14;
+
+    static final int DRAW = FOREGROUND | LINE_STYLE | LINE_WIDTH | LINE_CAP | LINE_JOIN | LINE_MITERLIMIT | PEN | NULL_BRUSH | DRAW_OFFSET;
+    static final int FILL = BACKGROUND | BRUSH | NULL_PEN;
+
+    static final float[] LINE_DOT_ZERO = new float[]{3, 3};
+    static final float[] LINE_DASH_ZERO = new float[]{18, 6};
+    static final float[] LINE_DASHDOT_ZERO = new float[]{9, 6, 3, 6};
+    static final float[] LINE_DASHDOTDOT_ZERO = new float[]{9, 3, 3, 3, 3, 3};
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+GC() {
+}
+
+/**
+ * Constructs a new instance of this class which has been
+ * configured to draw on the specified drawable. Sets the
+ * foreground color, background color and font in the GC
+ * to match those in the drawable.
+ * <p>
+ * You must dispose the graphics context when it is no longer required.
+ * </p>
+ * @param drawable the drawable to draw on
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the drawable is null</li>
+ *    <li>ERROR_NULL_ARGUMENT - if there is no current device</li>
+ *    <li>ERROR_INVALID_ARGUMENT
+ *          - if the drawable is an image that is not a bitmap or an icon
+ *          - if the drawable is an image or printer that is already selected
+ *            into another graphics context</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li>
+ * </ul>
+ */
+public GC(Drawable drawable) {
+    this(drawable, DWT.NONE);
+}
+
+/**
+ * Constructs a new instance of this class which has been
+ * configured to draw on the specified drawable. Sets the
+ * foreground color, background color and font in the GC
+ * to match those in the drawable.
+ * <p>
+ * You must dispose the graphics context when it is no longer required.
+ * </p>
+ *
+ * @param drawable the drawable to draw on
+ * @param style the style of GC to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the drawable is null</li>
+ *    <li>ERROR_NULL_ARGUMENT - if there is no current device</li>
+ *    <li>ERROR_INVALID_ARGUMENT
+ *          - if the drawable is an image that is not a bitmap or an icon
+ *          - if the drawable is an image or printer that is already selected
+ *            into another graphics context</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public GC(Drawable drawable, int style) {
+    if (drawable is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    GCData data = new GCData ();
+    data.style = checkStyle(style);
+    int hDC = drawable.internal_new_GC(data);
+    Device device = data.device;
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    this.device = data.device = device;
+    init (drawable, data, hDC);
+    if (device.tracking) device.new_Object(this);
+}
+
+static int checkStyle(int style) {
+    if ((style & DWT.LEFT_TO_RIGHT) !is 0) style &= ~DWT.RIGHT_TO_LEFT;
+    return style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT);
+}
+
+void checkGC(int mask) {
+    int state = data.state;
+    if ((state & mask) is mask) return;
+    state = (state ^ mask) & mask;
+    data.state |= mask;
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        int pen = data.gdipPen;
+        float width = data.lineWidth;
+        if ((state & FOREGROUND) !is 0 || (pen is 0 && (state & (LINE_WIDTH | LINE_STYLE | LINE_MITERLIMIT | LINE_JOIN | LINE_CAP)) !is 0)) {
+            if (data.gdipFgBrush !is 0) Gdip.SolidBrush_delete(data.gdipFgBrush);
+            data.gdipFgBrush = 0;
+            int brush;
+            Pattern pattern = data.foregroundPattern;
+            if (pattern !is null) {
+                brush = pattern.handle;
+                if ((data.style & DWT.MIRRORED) !is 0) {
+                    switch (Gdip.Brush_GetType(brush)) {
+                        case Gdip.BrushTypeTextureFill:
+                            brush = Gdip.Brush_Clone(brush);
+                            if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                            Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+                            data.gdipFgBrush = brush;
+                    }
+                }
+            } else {
+                int foreground = data.foreground;
+                int rgb = ((foreground >> 16) & 0xFF) | (foreground & 0xFF00) | ((foreground & 0xFF) << 16);
+                int color = Gdip.Color_new(data.alpha << 24 | rgb);
+                if (color is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                brush = Gdip.SolidBrush_new(color);
+                if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                Gdip.Color_delete(color);
+                data.gdipFgBrush = brush;
+            }
+            if (pen !is 0) {
+                Gdip.Pen_SetBrush(pen, brush);
+            } else {
+                pen = data.gdipPen = Gdip.Pen_new(brush, width);
+            }
+        }
+        if ((state & LINE_WIDTH) !is 0) {
+            Gdip.Pen_SetWidth(pen, width);
+            switch (data.lineStyle) {
+                case DWT.LINE_CUSTOM:
+                    state |= LINE_STYLE;
+            }
+        }
+        if ((state & LINE_STYLE) !is 0) {
+            float[] dashes = null;
+            float dashOffset = 0;
+            int dashStyle = Gdip.DashStyleSolid;
+            switch (data.lineStyle) {
+                case DWT.LINE_SOLID: break;
+                case DWT.LINE_DOT: dashStyle = Gdip.DashStyleDot; if (width is 0) dashes = LINE_DOT_ZERO; break;
+                case DWT.LINE_DASH: dashStyle = Gdip.DashStyleDash; if (width is 0) dashes = LINE_DASH_ZERO; break;
+                case DWT.LINE_DASHDOT: dashStyle = Gdip.DashStyleDashDot; if (width is 0) dashes = LINE_DASHDOT_ZERO; break;
+                case DWT.LINE_DASHDOTDOT: dashStyle = Gdip.DashStyleDashDotDot; if (width is 0) dashes = LINE_DASHDOTDOT_ZERO; break;
+                case DWT.LINE_CUSTOM: {
+                    if (data.lineDashes !is null) {
+                        dashOffset = data.lineDashesOffset / Math.max (1, width);
+                        dashes = new float[data.lineDashes.length * 2];
+                        for (int i = 0; i < data.lineDashes.length; i++) {
+                            float dash = data.lineDashes[i] / Math.max (1, width);
+                            dashes[i] = dash;
+                            dashes[i + data.lineDashes.length] = dash;
+                        }
+                    }
+                }
+            }
+            if (dashes !is null) {
+                Gdip.Pen_SetDashPattern(pen, dashes, dashes.length);
+                Gdip.Pen_SetDashStyle(pen, Gdip.DashStyleCustom);
+                Gdip.Pen_SetDashOffset(pen, dashOffset);
+            } else {
+                Gdip.Pen_SetDashStyle(pen, dashStyle);
+            }
+        }
+        if ((state & LINE_MITERLIMIT) !is 0) {
+            Gdip.Pen_SetMiterLimit(pen, data.lineMiterLimit);
+        }
+        if ((state & LINE_JOIN) !is 0) {
+            int joinStyle = 0;
+            switch (data.lineJoin) {
+                case DWT.JOIN_MITER: joinStyle = Gdip.LineJoinMiter; break;
+                case DWT.JOIN_BEVEL: joinStyle = Gdip.LineJoinBevel; break;
+                case DWT.JOIN_ROUND: joinStyle = Gdip.LineJoinRound; break;
+            }
+            Gdip.Pen_SetLineJoin(pen, joinStyle);
+        }
+        if ((state & LINE_CAP) !is 0) {
+            int dashCap = Gdip.DashCapFlat, capStyle = 0;
+            switch (data.lineCap) {
+                case DWT.CAP_FLAT: capStyle = Gdip.LineCapFlat; break;
+                case DWT.CAP_ROUND: capStyle = Gdip.LineCapRound; dashCap = Gdip.DashCapRound; break;
+                case DWT.CAP_SQUARE: capStyle = Gdip.LineCapSquare; break;
+            }
+            Gdip.Pen_SetLineCap(pen, capStyle, capStyle, dashCap);
+        }
+        if ((state & BACKGROUND) !is 0) {
+            if (data.gdipBgBrush !is 0) Gdip.SolidBrush_delete(data.gdipBgBrush);
+            data.gdipBgBrush = 0;
+            Pattern pattern = data.backgroundPattern;
+            if (pattern !is null) {
+                data.gdipBrush = pattern.handle;
+                if ((data.style & DWT.MIRRORED) !is 0) {
+                    switch (Gdip.Brush_GetType(data.gdipBrush)) {
+                        case Gdip.BrushTypeTextureFill:
+                            int brush = Gdip.Brush_Clone(data.gdipBrush);
+                            if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                            Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+                            data.gdipBrush = data.gdipBgBrush = brush;
+                    }
+                }
+            } else {
+                int background = data.background;
+                int rgb = ((background >> 16) & 0xFF) | (background & 0xFF00) | ((background & 0xFF) << 16);
+                int color = Gdip.Color_new(data.alpha << 24 | rgb);
+                if (color is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                int brush = Gdip.SolidBrush_new(color);
+                if (brush is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                Gdip.Color_delete(color);
+                data.gdipBrush = data.gdipBgBrush = brush;
+            }
+        }
+        if ((state & FONT) !is 0) {
+            OS.SelectObject(handle, data.hFont);
+            int font = createGdipFont(handle, data.hFont);
+            if (data.gdipFont !is 0) Gdip.Font_delete(data.gdipFont);
+            data.gdipFont = font;
+        }
+        if ((state & DRAW_OFFSET) !is 0) {
+            data.gdipXOffset = data.gdipYOffset = 0;
+            int matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
+            float[] elements = new float[6];
+            Gdip.Graphics_GetTransform(gdipGraphics, matrix);
+            Gdip.Matrix_GetElements(matrix, elements);
+            Gdip.Matrix_delete(matrix);
+            float scaling = elements[0];
+            if (scaling < 0) scaling = -scaling;
+            float penWidth = data.lineWidth * scaling;
+            if (penWidth is 0 || ((int)penWidth % 2) is 1) {
+                data.gdipXOffset = 0.5f / scaling;
+            }
+            scaling = elements[3];
+            if (scaling < 0) scaling = -scaling;
+            penWidth = data.lineWidth * scaling;
+            if (penWidth is 0 || ((int)penWidth % 2) is 1) {
+                data.gdipYOffset = 0.5f / scaling;
+            }
+        }
+        return;
+    }
+    if ((state & (FOREGROUND | LINE_CAP | LINE_JOIN | LINE_STYLE | LINE_WIDTH)) !is 0) {
+        int color = data.foreground;
+        int width = (int)data.lineWidth;
+        int[] dashes = null;
+        int lineStyle = OS.PS_SOLID;
+        switch (data.lineStyle) {
+            case DWT.LINE_SOLID: break;
+            case DWT.LINE_DASH: lineStyle = OS.PS_DASH; break;
+            case DWT.LINE_DOT: lineStyle = OS.PS_DOT; break;
+            case DWT.LINE_DASHDOT: lineStyle = OS.PS_DASHDOT; break;
+            case DWT.LINE_DASHDOTDOT: lineStyle = OS.PS_DASHDOTDOT; break;
+            case DWT.LINE_CUSTOM: {
+                if (data.lineDashes !is null) {
+                    lineStyle = OS.PS_USERSTYLE;
+                    dashes = new int[data.lineDashes.length];
+                    for (int i = 0; i < dashes.length; i++) {
+                        dashes[i] = (int)data.lineDashes[i];
+                    }
+                }
+                break;
+            }
+        }
+        if ((state & LINE_STYLE) !is 0) {
+            OS.SetBkMode(handle, data.lineStyle is DWT.LINE_SOLID ? OS.OPAQUE : OS.TRANSPARENT);
+        }
+        int joinStyle = 0;
+        switch (data.lineJoin) {
+            case DWT.JOIN_MITER: joinStyle = OS.PS_JOIN_MITER; break;
+            case DWT.JOIN_ROUND: joinStyle = OS.PS_JOIN_ROUND; break;
+            case DWT.JOIN_BEVEL: joinStyle = OS.PS_JOIN_BEVEL; break;
+        }
+        int capStyle = 0;
+        switch (data.lineCap) {
+            case DWT.CAP_ROUND: capStyle = OS.PS_ENDCAP_ROUND; break;
+            case DWT.CAP_FLAT: capStyle = OS.PS_ENDCAP_FLAT; break;
+            case DWT.CAP_SQUARE: capStyle = OS.PS_ENDCAP_SQUARE;break;
+        }
+        int style = lineStyle | joinStyle | capStyle;
+        /*
+        * Feature in Windows.  Windows does not honour line styles other then
+        * PS_SOLID for pens wider than 1 pixel created with CreatePen().  The fix
+        * is to use ExtCreatePen() instead.
+        */
+        int newPen;
+        if (OS.IsWinCE || (width is 0 && lineStyle !is OS.PS_USERSTYLE) || style is 0) {
+            newPen = OS.CreatePen(style & OS.PS_STYLE_MASK, width, color);
+        } else {
+            LOGBRUSH logBrush = new LOGBRUSH();
+            logBrush.lbStyle = OS.BS_SOLID;
+            logBrush.lbColor = color;
+            /* Feature in Windows. PS_GEOMETRIC pens cannot have zero width. */
+            newPen = OS.ExtCreatePen (style | OS.PS_GEOMETRIC, Math.max(1, width), logBrush, dashes !is null ? dashes.length : 0, dashes);
+        }
+        OS.SelectObject(handle, newPen);
+        data.state |= PEN;
+        data.state &= ~NULL_PEN;
+        if (data.hPen !is 0) OS.DeleteObject(data.hPen);
+        data.hPen = data.hOldPen = newPen;
+    } else if ((state & PEN) !is 0) {
+        OS.SelectObject(handle, data.hOldPen);
+        data.state &= ~NULL_PEN;
+    } else if ((state & NULL_PEN) !is 0) {
+        data.hOldPen = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN));
+        data.state &= ~PEN;
+    }
+    if ((state & BACKGROUND) !is 0) {
+        int newBrush = OS.CreateSolidBrush(data.background);
+        OS.SelectObject(handle, newBrush);
+        data.state |= BRUSH;
+        data.state &= ~NULL_BRUSH;
+        if (data.hBrush !is 0) OS.DeleteObject(data.hBrush);
+        data.hOldBrush = data.hBrush = newBrush;
+    } else if ((state & BRUSH) !is 0) {
+        OS.SelectObject(handle, data.hOldBrush);
+        data.state &= ~NULL_BRUSH;
+    } else if ((state & NULL_BRUSH) !is 0) {
+        data.hOldBrush = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH));
+        data.state &= ~BRUSH;
+    }
+    if ((state & BACKGROUND_TEXT) !is 0) {
+        OS.SetBkColor(handle, data.background);
+    }
+    if ((state & FOREGROUND_TEXT) !is 0) {
+        OS.SetTextColor(handle, data.foreground);
+    }
+    if ((state & FONT) !is 0) {
+        OS.SelectObject(handle, data.hFont);
+    }
+}
+
+/**
+ * Copies a rectangular area of the receiver at the specified
+ * position into the image, which must be of type <code>DWT.BITMAP</code>.
+ *
+ * @param image the image to copy into
+ * @param x the x coordinate in the receiver of the area to be copied
+ * @param y the y coordinate in the receiver of the area to be copied
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image is not a bitmap or has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void copyArea(Image image, int x, int y) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (image is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (image.type !is DWT.BITMAP || image.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+
+    /* Copy the bitmap area */
+    Rectangle rect = image.getBounds();
+    int memHdc = OS.CreateCompatibleDC(handle);
+    int hOldBitmap = OS.SelectObject(memHdc, image.handle);
+    OS.BitBlt(memHdc, 0, 0, rect.width, rect.height, handle, x, y, OS.SRCCOPY);
+    OS.SelectObject(memHdc, hOldBitmap);
+    OS.DeleteDC(memHdc);
+}
+
+/**
+ * Copies a rectangular area of the receiver at the source
+ * position onto the receiver at the destination position.
+ *
+ * @param srcX the x coordinate in the receiver of the area to be copied
+ * @param srcY the y coordinate in the receiver of the area to be copied
+ * @param width the width of the area to copy
+ * @param height the height of the area to copy
+ * @param destX the x coordinate in the receiver of the area to copy to
+ * @param destY the y coordinate in the receiver of the area to copy to
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY) {
+    copyArea(srcX, srcY, width, height, destX, destY, true);
+}
+
+/**
+ * Copies a rectangular area of the receiver at the source
+ * position onto the receiver at the destination position.
+ *
+ * @param srcX the x coordinate in the receiver of the area to be copied
+ * @param srcY the y coordinate in the receiver of the area to be copied
+ * @param width the width of the area to copy
+ * @param height the height of the area to copy
+ * @param destX the x coordinate in the receiver of the area to copy to
+ * @param destY the y coordinate in the receiver of the area to copy to
+ * @param paint if <code>true</code> paint events will be generated for old and obscured areas
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void copyArea(int srcX, int srcY, int width, int height, int destX, int destY, bool paint) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+
+    /*
+    * Feature in WinCE.  The function WindowFromDC is not part of the
+    * WinCE SDK.  The fix is to remember the HWND.
+    */
+    int hwnd = data.hwnd;
+    if (hwnd is 0) {
+        OS.BitBlt(handle, destX, destY, width, height, handle, srcX, srcY, OS.SRCCOPY);
+    } else {
+        RECT lprcClip = null;
+        int hrgn = OS.CreateRectRgn(0, 0, 0, 0);
+        if (OS.GetClipRgn(handle, hrgn) is 1) {
+            lprcClip = new RECT();
+            OS.GetRgnBox(hrgn, lprcClip);
+        }
+        OS.DeleteObject(hrgn);
+        RECT lprcScroll = new RECT();
+        OS.SetRect(lprcScroll, srcX, srcY, srcX + width, srcY + height);
+        int flags = paint ? OS.SW_INVALIDATE | OS.SW_ERASE : 0;
+        int res = OS.ScrollWindowEx(hwnd, destX - srcX, destY - srcY, lprcScroll, lprcClip, 0, null, flags);
+
+        /*
+        * Feature in WinCE.  ScrollWindowEx does not accept combined
+        * vertical and horizontal scrolling.  The fix is to do a
+        * BitBlt and invalidate the appropriate source area.
+        */
+        if (res is 0 && OS.IsWinCE) {
+            OS.BitBlt(handle, destX, destY, width, height, handle, srcX, srcY, OS.SRCCOPY);
+            if (paint) {
+                int deltaX = destX - srcX, deltaY = destY - srcY;
+                bool disjoint = (destX + width < srcX) || (srcX + width < destX) || (destY + height < srcY) || (srcY + height < destY);
+                if (disjoint) {
+                    OS.InvalidateRect(hwnd, lprcScroll, true);
+                } else {
+                    if (deltaX !is 0) {
+                        int newX = destX - deltaX;
+                        if (deltaX < 0) newX = destX + width;
+                        OS.SetRect(lprcScroll, newX, srcY, newX + Math.abs(deltaX), srcY + height);
+                        OS.InvalidateRect(hwnd, lprcScroll, true);
+                    }
+                    if (deltaY !is 0) {
+                        int newY = destY - deltaY;
+                        if (deltaY < 0) newY = destY + height;
+                        OS.SetRect(lprcScroll, srcX, newY, srcX + width, newY + Math.abs(deltaY));
+                        OS.InvalidateRect(hwnd, lprcScroll, true);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static int createGdipFont(int hDC, int hFont) {
+    int font = Gdip.Font_new(hDC, hFont);
+    if (font is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    if (!Gdip.Font_IsAvailable(font)) {
+        Gdip.Font_delete(font);
+        LOGFONT logFont = OS.IsUnicode ? (LOGFONT)new LOGFONTW() : new LOGFONTA();
+        OS.GetObject(hFont, LOGFONT.sizeof, logFont);
+        int size = Math.abs(logFont.lfHeight);
+        int style = Gdip.FontStyleRegular;
+        if (logFont.lfWeight is 700) style |= Gdip.FontStyleBold;
+        if (logFont.lfItalic !is 0) style |= Gdip.FontStyleItalic;
+        char[] chars;
+        if (OS.IsUnicode) {
+            chars = ((LOGFONTW)logFont).lfFaceName;
+        } else {
+            chars = new char[OS.LF_FACESIZE];
+            byte[] bytes = ((LOGFONTA)logFont).lfFaceName;
+            OS.MultiByteToWideChar (OS.CP_ACP, OS.MB_PRECOMPOSED, bytes, bytes.length, chars, chars.length);
+        }
+        int index = 0;
+        while (index < chars.length) {
+            if (chars [index] is 0) break;
+            index++;
+        }
+        String name = new String (chars, 0, index);
+        if (Compatibility.equalsIgnoreCase(name, "Courier")) { //$NON-NLS-1$
+            name = "Courier New"; //$NON-NLS-1$
+        }
+        char[] buffer = new char[name.length() + 1];
+        name.getChars(0, name.length(), buffer, 0);
+        font = Gdip.Font_new(buffer, size, style, Gdip.UnitPixel, 0);
+    }
+    if (font is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    return font;
+}
+
+static void destroyGdipBrush(int brush) {
+    int type = Gdip.Brush_GetType(brush);
+    switch (type) {
+        case Gdip.BrushTypeSolidColor:
+            Gdip.SolidBrush_delete(brush);
+            break;
+        case Gdip.BrushTypeHatchFill:
+            Gdip.HatchBrush_delete(brush);
+            break;
+        case Gdip.BrushTypeLinearGradient:
+            Gdip.LinearGradientBrush_delete(brush);
+            break;
+        case Gdip.BrushTypeTextureFill:
+            Gdip.TextureBrush_delete(brush);
+            break;
+    }
+}
+
+/**
+ * Disposes of the operating system resources associated with
+ * the graphics context. Applications must dispose of all GCs
+ * which they allocate.
+ *
+ * @exception DWTError <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li>
+ * </ul>
+ */
+public void dispose() {
+    if (handle is 0) return;
+    if (data.device.isDisposed()) return;
+
+    disposeGdip();
+
+    /* Select stock pen and brush objects and free resources */
+    if (data.hPen !is 0) {
+        OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN));
+        OS.DeleteObject(data.hPen);
+        data.hPen = 0;
+    }
+    if (data.hBrush !is 0) {
+        OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH));
+        OS.DeleteObject(data.hBrush);
+        data.hBrush = 0;
+    }
+
+    /*
+    * Put back the original bitmap into the device context.
+    * This will ensure that we have not left a bitmap
+    * selected in it when we delete the HDC.
+    */
+    int hNullBitmap = data.hNullBitmap;
+    if (hNullBitmap !is 0) {
+        OS.SelectObject(handle, hNullBitmap);
+        data.hNullBitmap = 0;
+    }
+    Image image = data.image;
+    if (image !is null) image.memGC = null;
+
+    /*
+    * Dispose the HDC.
+    */
+    Device device = data.device;
+    if (drawable !is null) drawable.internal_dispose_GC(handle, data);
+    drawable = null;
+    handle = 0;
+    data.image = null;
+    data.ps = null;
+    if (device.tracking) device.dispose_Object(this);
+    data.device = null;
+    data = null;
+}
+
+void disposeGdip() {
+    if (data.gdipPen !is 0) Gdip.Pen_delete(data.gdipPen);
+    if (data.gdipBgBrush !is 0) destroyGdipBrush(data.gdipBgBrush);
+    if (data.gdipFgBrush !is 0) destroyGdipBrush(data.gdipFgBrush);
+    if (data.gdipFont !is 0) Gdip.Font_delete(data.gdipFont);
+    if (data.gdipGraphics !is 0) Gdip.Graphics_delete(data.gdipGraphics);
+    data.gdipGraphics = data.gdipBrush = data.gdipBgBrush = data.gdipFgBrush =
+        data.gdipFont = data.gdipPen = 0;
+}
+
+/**
+ * Draws the outline of a circular or elliptical arc
+ * within the specified rectangular area.
+ * <p>
+ * The resulting arc begins at <code>startAngle</code> and extends
+ * for <code>arcAngle</code> degrees, using the current color.
+ * Angles are interpreted such that 0 degrees is at the 3 o'clock
+ * position. A positive value indicates a counter-clockwise rotation
+ * while a negative value indicates a clockwise rotation.
+ * </p><p>
+ * The center of the arc is the center of the rectangle whose origin
+ * is (<code>x</code>, <code>y</code>) and whose size is specified by the
+ * <code>width</code> and <code>height</code> arguments.
+ * </p><p>
+ * The resulting arc covers an area <code>width + 1</code> pixels wide
+ * by <code>height + 1</code> pixels tall.
+ * </p>
+ *
+ * @param x the x coordinate of the upper-left corner of the arc to be drawn
+ * @param y the y coordinate of the upper-left corner of the arc to be drawn
+ * @param width the width of the arc to be drawn
+ * @param height the height of the arc to be drawn
+ * @param startAngle the beginning angle
+ * @param arcAngle the angular extent of the arc, relative to the start angle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawArc (int x, int y, int width, int height, int startAngle, int arcAngle) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(DRAW);
+    if (width < 0) {
+        x = x + width;
+        width = -width;
+    }
+    if (height < 0) {
+        y = y + height;
+        height = -height;
+    }
+    if (width is 0 || height is 0 || arcAngle is 0) return;
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        if (width is height) {
+            Gdip.Graphics_DrawArc(gdipGraphics, data.gdipPen, x, y, width, height, -startAngle, -arcAngle);
+        } else {
+            int path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate);
+            if (path is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            int matrix = Gdip.Matrix_new(width, 0, 0, height, x, y);
+            if (matrix is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            Gdip.GraphicsPath_AddArc(path, 0, 0, 1, 1, -startAngle, -arcAngle);
+            Gdip.GraphicsPath_Transform(path, matrix);
+            Gdip.Graphics_DrawPath(gdipGraphics, data.gdipPen, path);
+            Gdip.Matrix_delete(matrix);
+            Gdip.GraphicsPath_delete(path);
+        }
+        Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) x--;
+    }
+    /*
+    * Feature in WinCE.  The function Arc is not present in the
+    * WinCE SDK.  The fix is to emulate arc drawing by using
+    * Polyline.
+    */
+    if (OS.IsWinCE) {
+        /* compute arc with a simple linear interpolation */
+        if (arcAngle < 0) {
+            startAngle += arcAngle;
+            arcAngle = -arcAngle;
+        }
+        if (arcAngle > 360) arcAngle = 360;
+        int[] points = new int[(arcAngle + 1) * 2];
+        int cteX = 2 * x + width;
+        int cteY = 2 * y + height;
+        int index = 0;
+        for (int i = 0; i <= arcAngle; i++) {
+            points[index++] = (Compatibility.cos(startAngle + i, width) + cteX) >> 1;
+            points[index++] = (cteY - Compatibility.sin(startAngle + i, height)) >> 1;
+        }
+        OS.Polyline(handle, points, points.length / 2);
+    } else {
+        int x1, y1, x2, y2,tmp;
+        bool isNegative;
+        if (arcAngle >= 360 || arcAngle <= -360) {
+            x1 = x2 = x + width;
+            y1 = y2 = y + height / 2;
+        } else {
+            isNegative = arcAngle < 0;
+
+            arcAngle = arcAngle + startAngle;
+            if (isNegative) {
+                // swap angles
+                tmp = startAngle;
+                startAngle = arcAngle;
+                arcAngle = tmp;
+            }
+            x1 = Compatibility.cos(startAngle, width) + x + width/2;
+            y1 = -1 * Compatibility.sin(startAngle, height) + y + height/2;
+
+            x2 = Compatibility.cos(arcAngle, width) + x + width/2;
+            y2 = -1 * Compatibility.sin(arcAngle, height) + y + height/2;
+        }
+        OS.Arc(handle, x, y, x + width + 1, y + height + 1, x1, y1, x2, y2);
+    }
+}
+
+/**
+ * Draws a rectangle, based on the specified arguments, which has
+ * the appearance of the platform's <em>focus rectangle</em> if the
+ * platform supports such a notion, and otherwise draws a simple
+ * rectangle in the receiver's foreground color.
+ *
+ * @param x the x coordinate of the rectangle
+ * @param y the y coordinate of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawRectangle(int, int, int, int)
+ */
+public void drawFocus (int x, int y, int width, int height) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if ((data.uiState & OS.UISF_HIDEFOCUS) !is 0) return;
+    int hdc = handle, state = 0;
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        int clipRgn = 0;
+        Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
+        int rgn = Gdip.Region_new();
+        if (rgn is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        Gdip.Graphics_GetClip(gdipGraphics, rgn);
+        if (!Gdip.Region_IsInfinite(rgn, gdipGraphics)) {
+            clipRgn = Gdip.Region_GetHRGN(rgn, gdipGraphics);
+        }
+        Gdip.Region_delete(rgn);
+        Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
+        float[] lpXform = null;
+        int matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
+        if (matrix is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        Gdip.Graphics_GetTransform(gdipGraphics, matrix);
+        if (!Gdip.Matrix_IsIdentity(matrix)) {
+            lpXform = new float[6];
+            Gdip.Matrix_GetElements(matrix, lpXform);
+        }
+        Gdip.Matrix_delete(matrix);
+        hdc = Gdip.Graphics_GetHDC(gdipGraphics);
+        state = OS.SaveDC(hdc);
+        OS.SetBkColor(hdc, data.background);
+        OS.SetTextColor(hdc, data.foreground);
+        if (lpXform !is null) {
+            OS.SetGraphicsMode(hdc, OS.GM_ADVANCED);
+            OS.SetWorldTransform(hdc, lpXform);
+        }
+        if (clipRgn !is 0) {
+            OS.SelectClipRgn(hdc, clipRgn);
+            OS.DeleteObject(clipRgn);
+        }
+    }
+    RECT rect = new RECT();
+    OS.SetRect(rect, x, y, x + width, y + height);
+    OS.DrawFocusRect(hdc, rect);
+    if (gdipGraphics !is 0) {
+        OS.RestoreDC(hdc, state);
+        Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc);
+    }
+}
+
+/**
+ * Draws the given image in the receiver at the specified
+ * coordinates.
+ *
+ * @param image the image to draw
+ * @param x the x coordinate of where to draw
+ * @param y the y coordinate of where to draw
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the given coordinates are outside the bounds of the image</li>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES - if no handles are available to perform the operation</li>
+ * </ul>
+ */
+public void drawImage(Image image, int x, int y) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (image is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    if (image.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    drawImage(image, 0, 0, -1, -1, x, y, -1, -1, true);
+}
+
+/**
+ * Copies a rectangular area from the source image into a (potentially
+ * different sized) rectangular area in the receiver. If the source
+ * and destination areas are of differing sizes, then the source
+ * area will be stretched or shrunk to fit the destination area
+ * as it is copied. The copy fails if any part of the source rectangle
+ * lies outside the bounds of the source image, or if any of the width
+ * or height arguments are negative.
+ *
+ * @param image the source image
+ * @param srcX the x coordinate in the source image to copy from
+ * @param srcY the y coordinate in the source image to copy from
+ * @param srcWidth the width in pixels to copy from the source
+ * @param srcHeight the height in pixels to copy from the source
+ * @param destX the x coordinate in the destination to copy to
+ * @param destY the y coordinate in the destination to copy to
+ * @param destWidth the width in pixels of the destination rectangle
+ * @param destHeight the height in pixels of the destination rectangle
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if any of the width or height arguments are negative.
+ *    <li>ERROR_INVALID_ARGUMENT - if the source rectangle is not contained within the bounds of the source image</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES - if no handles are available to perform the operation</li>
+ * </ul>
+ */
+public void drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (srcWidth is 0 || srcHeight is 0 || destWidth is 0 || destHeight is 0) return;
+    if (srcX < 0 || srcY < 0 || srcWidth < 0 || srcHeight < 0 || destWidth < 0 || destHeight < 0) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    if (image is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    if (image.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    drawImage(image, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, false);
+}
+
+void drawImage(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple) {
+    if (data.gdipGraphics !is 0) {
+        //TODO - cache bitmap
+        int[] gdipImage = srcImage.createGdipImage();
+        int img = gdipImage[0];
+        int imgWidth = Gdip.Image_GetWidth(img);
+        int imgHeight = Gdip.Image_GetHeight(img);
+        if (simple) {
+            srcWidth = destWidth = imgWidth;
+            srcHeight = destHeight = imgHeight;
+        } else {
+            if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) {
+                DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+            }
+            simple = srcX is 0 && srcY is 0 &&
+                srcWidth is destWidth && destWidth is imgWidth &&
+                srcHeight is destHeight && destHeight is imgHeight;
+        }
+        Rect rect = new Rect();
+        rect.X = destX;
+        rect.Y = destY;
+        rect.Width = destWidth;
+        rect.Height = destHeight;
+        /*
+        * Note that if the wrap mode is not WrapModeTileFlipXY, the scaled image
+        * is translucent around the borders.
+        */
+        int attrib = Gdip.ImageAttributes_new();
+        Gdip.ImageAttributes_SetWrapMode(attrib, Gdip.WrapModeTileFlipXY);
+        if (data.alpha !is 0xFF) {
+            float[] matrix = new float[]{
+                1,0,0,0,0,
+                0,1,0,0,0,
+                0,0,1,0,0,
+                0,0,0,data.alpha / (float)0xFF,0,
+                0,0,0,0,1,
+            };
+            Gdip.ImageAttributes_SetColorMatrix(attrib, matrix, Gdip.ColorMatrixFlagsDefault, Gdip.ColorAdjustTypeBitmap);
+        }
+        int gstate = 0;
+        if ((data.style & DWT.MIRRORED) !is 0) {
+            gstate = Gdip.Graphics_Save(data.gdipGraphics);
+            Gdip.Graphics_ScaleTransform(data.gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend);
+            Gdip.Graphics_TranslateTransform(data.gdipGraphics, - 2 * destX - destWidth, 0, Gdip.MatrixOrderPrepend);
+        }
+        Gdip.Graphics_DrawImage(data.gdipGraphics, img, rect, srcX, srcY, srcWidth, srcHeight, Gdip.UnitPixel, attrib, 0, 0);
+        if ((data.style & DWT.MIRRORED) !is 0) {
+            Gdip.Graphics_Restore(data.gdipGraphics, gstate);
+        }
+        Gdip.ImageAttributes_delete(attrib);
+        Gdip.Bitmap_delete(img);
+        if (gdipImage[1] !is 0) {
+            int hHeap = OS.GetProcessHeap ();
+            OS.HeapFree(hHeap, 0, gdipImage[1]);
+        }
+        return;
+    }
+    switch (srcImage.type) {
+        case DWT.BITMAP:
+            drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
+            break;
+        case DWT.ICON:
+            drawIcon(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple);
+            break;
+    }
+}
+
+void drawIcon(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple) {
+    int technology = OS.GetDeviceCaps(handle, OS.TECHNOLOGY);
+
+    bool drawIcon = true;
+    int flags = OS.DI_NORMAL;
+    int offsetX = 0, offsetY = 0;
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(5, 1)) {
+        if ((OS.GetLayout(handle) & OS.LAYOUT_RTL) !is 0) {
+            flags |= OS.DI_NOMIRROR;
+            /*
+            * Bug in Windows.  For some reason, DrawIconEx() does not take
+            * into account the window origin when the DI_NOMIRROR and
+            * LAYOUT_RTL are set.  The fix is to set the window origin to
+            * (0, 0) and offset the drawing ourselves.
+            */
+            POINT pt = new POINT();
+            OS.GetWindowOrgEx(handle, pt);
+            offsetX = pt.x;
+            offsetY = pt.y;
+        }
+    } else {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
+            drawIcon = (OS.GetLayout(handle) & OS.LAYOUT_RTL) is 0;
+        }
+    }
+
+    /* Simple case: no stretching, entire icon */
+    if (simple && technology !is OS.DT_RASPRINTER && drawIcon) {
+        if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, 0, 0, null);
+        OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, srcImage.handle, 0, 0, 0, 0, flags);
+        if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null);
+        return;
+    }
+
+    /* Get the icon info */
+    ICONINFO srcIconInfo = new ICONINFO();
+    if (OS.IsWinCE) {
+        Image.GetIconInfo(srcImage, srcIconInfo);
+    } else {
+        OS.GetIconInfo(srcImage.handle, srcIconInfo);
+    }
+
+    /* Get the icon width and height */
+    int hBitmap = srcIconInfo.hbmColor;
+    if (hBitmap is 0) hBitmap = srcIconInfo.hbmMask;
+    BITMAP bm = new BITMAP();
+    OS.GetObject(hBitmap, BITMAP.sizeof, bm);
+    int iconWidth = bm.bmWidth, iconHeight = bm.bmHeight;
+    if (hBitmap is srcIconInfo.hbmMask) iconHeight /= 2;
+
+    if (simple) {
+        srcWidth = destWidth = iconWidth;
+        srcHeight = destHeight = iconHeight;
+    }
+
+    /* Draw the icon */
+    bool failed = srcX + srcWidth > iconWidth || srcY + srcHeight > iconHeight;
+    if (!failed) {
+        simple = srcX is 0 && srcY is 0 &&
+            srcWidth is destWidth && srcHeight is destHeight &&
+            srcWidth is iconWidth && srcHeight is iconHeight;
+        if (!drawIcon) {
+            drawBitmapMask(srcImage, srcIconInfo.hbmColor, srcIconInfo.hbmMask, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, iconWidth, iconHeight, false);
+        } else if (simple && technology !is OS.DT_RASPRINTER) {
+            /* Simple case: no stretching, entire icon */
+            if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, 0, 0, null);
+            OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, srcImage.handle, 0, 0, 0, 0, flags);
+            if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null);
+        } else {
+            /* Create the icon info and HDC's */
+            ICONINFO newIconInfo = new ICONINFO();
+            newIconInfo.fIcon = true;
+            int srcHdc = OS.CreateCompatibleDC(handle);
+            int dstHdc = OS.CreateCompatibleDC(handle);
+
+            /* Blt the color bitmap */
+            int srcColorY = srcY;
+            int srcColor = srcIconInfo.hbmColor;
+            if (srcColor is 0) {
+                srcColor = srcIconInfo.hbmMask;
+                srcColorY += iconHeight;
+            }
+            int oldSrcBitmap = OS.SelectObject(srcHdc, srcColor);
+            newIconInfo.hbmColor = OS.CreateCompatibleBitmap(srcHdc, destWidth, destHeight);
+            if (newIconInfo.hbmColor is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            int oldDestBitmap = OS.SelectObject(dstHdc, newIconInfo.hbmColor);
+            bool stretch = !simple && (srcWidth !is destWidth || srcHeight !is destHeight);
+            if (stretch) {
+                if (!OS.IsWinCE) OS.SetStretchBltMode(dstHdc, OS.COLORONCOLOR);
+                OS.StretchBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcColorY, srcWidth, srcHeight, OS.SRCCOPY);
+            } else {
+                OS.BitBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcColorY, OS.SRCCOPY);
+            }
+
+            /* Blt the mask bitmap */
+            OS.SelectObject(srcHdc, srcIconInfo.hbmMask);
+            newIconInfo.hbmMask = OS.CreateBitmap(destWidth, destHeight, 1, 1, null);
+            if (newIconInfo.hbmMask is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            OS.SelectObject(dstHdc, newIconInfo.hbmMask);
+            if (stretch) {
+                OS.StretchBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCCOPY);
+            } else {
+                OS.BitBlt(dstHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCCOPY);
+            }
+
+            if (technology is OS.DT_RASPRINTER) {
+                OS.SelectObject(srcHdc, newIconInfo.hbmColor);
+                OS.SelectObject(dstHdc, newIconInfo.hbmMask);
+                drawBitmapTransparentByClipping(srcHdc, dstHdc, 0, 0, destWidth, destHeight, destX, destY, destWidth, destHeight, true, destWidth, destHeight);
+                OS.SelectObject(srcHdc, oldSrcBitmap);
+                OS.SelectObject(dstHdc, oldDestBitmap);
+            } else {
+                OS.SelectObject(srcHdc, oldSrcBitmap);
+                OS.SelectObject(dstHdc, oldDestBitmap);
+                int hIcon = OS.CreateIconIndirect(newIconInfo);
+                if (hIcon is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, 0, 0, null);
+                OS.DrawIconEx(handle, destX - offsetX, destY - offsetY, hIcon, destWidth, destHeight, 0, 0, flags);
+                if (offsetX !is 0 || offsetY !is 0) OS.SetWindowOrgEx(handle, offsetX, offsetY, null);
+                OS.DestroyIcon(hIcon);
+            }
+
+            /* Destroy the new icon src and mask and hdc's*/
+            OS.DeleteObject(newIconInfo.hbmMask);
+            OS.DeleteObject(newIconInfo.hbmColor);
+            OS.DeleteDC(dstHdc);
+            OS.DeleteDC(srcHdc);
+        }
+    }
+
+    /* Free icon info */
+    OS.DeleteObject(srcIconInfo.hbmMask);
+    if (srcIconInfo.hbmColor !is 0) {
+        OS.DeleteObject(srcIconInfo.hbmColor);
+    }
+
+    if (failed) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+}
+
+void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple) {
+    BITMAP bm = new BITMAP();
+    OS.GetObject(srcImage.handle, BITMAP.sizeof, bm);
+    int imgWidth = bm.bmWidth;
+    int imgHeight = bm.bmHeight;
+    if (simple) {
+        srcWidth = destWidth = imgWidth;
+        srcHeight = destHeight = imgHeight;
+    } else {
+        if (srcX + srcWidth > imgWidth || srcY + srcHeight > imgHeight) {
+            DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+        }
+        simple = srcX is 0 && srcY is 0 &&
+            srcWidth is destWidth && destWidth is imgWidth &&
+            srcHeight is destHeight && destHeight is imgHeight;
+    }
+    bool mustRestore = false;
+    GC memGC = srcImage.memGC;
+    if (memGC !is null && !memGC.isDisposed()) {
+        memGC.flush();
+        mustRestore = true;
+        GCData data = memGC.data;
+        if (data.hNullBitmap !is 0) {
+            OS.SelectObject(memGC.handle, data.hNullBitmap);
+            data.hNullBitmap = 0;
+        }
+    }
+    if (srcImage.alpha !is -1 || srcImage.alphaData !is null) {
+        drawBitmapAlpha(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+    } else if (srcImage.transparentPixel !is -1) {
+        drawBitmapTransparent(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+    } else {
+        drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+    }
+    if (mustRestore) {
+        int hOldBitmap = OS.SelectObject(memGC.handle, srcImage.handle);
+        memGC.data.hNullBitmap = hOldBitmap;
+    }
+}
+
+void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, BITMAP bm, int imgWidth, int imgHeight) {
+    /* Simple cases */
+    if (srcImage.alpha is 0) return;
+    if (srcImage.alpha is 255) {
+        drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight);
+        return;
+    }
+
+    if (OS.IsWinNT && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
+        BLENDFUNCTION blend = new BLENDFUNCTION();
+        blend.BlendOp = OS.AC_SRC_OVER;
+        int srcHdc = OS.CreateCompatibleDC(handle);
+        int oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle);
+        if (srcImage.alpha !is -1) {
+            blend.SourceConstantAlpha = (byte)srcImage.alpha;
+            OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, blend);
+        } else {
+            int memDib = Image.createDIB(srcWidth, srcHeight, 32);
+            if (memDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            int memHdc = OS.CreateCompatibleDC(handle);
+            int oldMemBitmap = OS.SelectObject(memHdc, memDib);
+            BITMAP dibBM = new BITMAP();
+            OS.GetObject(memDib, BITMAP.sizeof, dibBM);
+            OS.BitBlt(memHdc, 0, 0, srcWidth, srcHeight, srcHdc, srcX, srcY, OS.SRCCOPY);
+            byte[] srcData = new byte[dibBM.bmWidthBytes * dibBM.bmHeight];
+            OS.MoveMemory(srcData, dibBM.bmBits, srcData.length);
+            final int apinc = imgWidth - srcWidth;
+            int ap = srcY * imgWidth + srcX, sp = 0;
+            byte[] alphaData = srcImage.alphaData;
+            for (int y = 0; y < srcHeight; ++y) {
+                for (int x = 0; x < srcWidth; ++x) {
+                    int alpha = alphaData[ap++] & 0xff;
+                    int r = ((srcData[sp + 0] & 0xFF) * alpha) + 128;
+                    r = (r + (r >> 8)) >> 8;
+                    int g = ((srcData[sp + 1] & 0xFF) * alpha) + 128;
+                    g = (g + (g >> 8)) >> 8;
+                    int b = ((srcData[sp + 2] & 0xFF) * alpha) + 128;
+                    b = (b + (b >> 8)) >> 8;
+                    srcData[sp+0] = (byte)r;
+                    srcData[sp+1] = (byte)g;
+                    srcData[sp+2] = (byte)b;
+                    srcData[sp+3] = (byte)alpha;
+                    sp += 4;
+                }
+                ap += apinc;
+            }
+            OS.MoveMemory(dibBM.bmBits, srcData, srcData.length);
+            blend.SourceConstantAlpha = (byte)0xff;
+            blend.AlphaFormat = OS.AC_SRC_ALPHA;
+            OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, blend);
+            OS.SelectObject(memHdc, oldMemBitmap);
+            OS.DeleteDC(memHdc);
+            OS.DeleteObject(memDib);
+        }
+        OS.SelectObject(srcHdc, oldSrcBitmap);
+        OS.DeleteDC(srcHdc);
+        return;
+    }
+
+    /* Check clipping */
+    Rectangle rect = getClipping();
+    rect = rect.intersection(new Rectangle(destX, destY, destWidth, destHeight));
+    if (rect.isEmpty()) return;
+
+    /*
+    * Optimization.  Recalculate src and dest rectangles so that
+    * only the clipping area is drawn.
+    */
+    int sx1 = srcX + (((rect.x - destX) * srcWidth) / destWidth);
+    int sx2 = srcX + ((((rect.x + rect.width) - destX) * srcWidth) / destWidth);
+    int sy1 = srcY + (((rect.y - destY) * srcHeight) / destHeight);
+    int sy2 = srcY + ((((rect.y + rect.height) - destY) * srcHeight) / destHeight);
+    destX = rect.x;
+    destY = rect.y;
+    destWidth = rect.width;
+    destHeight = rect.height;
+    srcX = sx1;
+    srcY = sy1;
+    srcWidth = Math.max(1, sx2 - sx1);
+    srcHeight = Math.max(1, sy2 - sy1);
+
+    /* Create resources */
+    int srcHdc = OS.CreateCompatibleDC(handle);
+    int oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle);
+    int memHdc = OS.CreateCompatibleDC(handle);
+    int memDib = Image.createDIB(Math.max(srcWidth, destWidth), Math.max(srcHeight, destHeight), 32);
+    if (memDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    int oldMemBitmap = OS.SelectObject(memHdc, memDib);
+
+    BITMAP dibBM = new BITMAP();
+    OS.GetObject(memDib, BITMAP.sizeof, dibBM);
+    int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
+
+    /* Get the background pixels */
+    OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, handle, destX, destY, OS.SRCCOPY);
+    byte[] destData = new byte[sizeInBytes];
+    OS.MoveMemory(destData, dibBM.bmBits, sizeInBytes);
+
+    /* Get the foreground pixels */
+    OS.BitBlt(memHdc, 0, 0, srcWidth, srcHeight, srcHdc, srcX, srcY, OS.SRCCOPY);
+    byte[] srcData = new byte[sizeInBytes];
+    OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes);
+
+    /* Merge the alpha channel in place */
+    int alpha = srcImage.alpha;
+    final bool hasAlphaChannel = (srcImage.alpha is -1);
+    if (hasAlphaChannel) {
+        final int apinc = imgWidth - srcWidth;
+        final int spinc = dibBM.bmWidthBytes - srcWidth * 4;
+        int ap = srcY * imgWidth + srcX, sp = 3;
+        byte[] alphaData = srcImage.alphaData;
+        for (int y = 0; y < srcHeight; ++y) {
+            for (int x = 0; x < srcWidth; ++x) {
+                srcData[sp] = alphaData[ap++];
+                sp += 4;
+            }
+            ap += apinc;
+            sp += spinc;
+        }
+    }
+
+    /* Scale the foreground pixels with alpha */
+    OS.MoveMemory(dibBM.bmBits, srcData, sizeInBytes);
+    /*
+    * Bug in WinCE and Win98.  StretchBlt does not correctly stretch when
+    * the source and destination HDCs are the same.  The workaround is to
+    * stretch to a temporary HDC and blit back into the original HDC.
+    * Note that on WinCE StretchBlt correctly compresses the image when the
+    * source and destination HDCs are the same.
+    */
+    if ((OS.IsWinCE && (destWidth > srcWidth || destHeight > srcHeight)) || (!OS.IsWinNT && !OS.IsWinCE)) {
+        int tempHdc = OS.CreateCompatibleDC(handle);
+        int tempDib = Image.createDIB(destWidth, destHeight, 32);
+        if (tempDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        int oldTempBitmap = OS.SelectObject(tempHdc, tempDib);
+        if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+            if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc, OS.COLORONCOLOR);
+            OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
+        } else {
+            OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY);
+        }
+        OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, tempHdc, 0, 0, OS.SRCCOPY);
+        OS.SelectObject(tempHdc, oldTempBitmap);
+        OS.DeleteObject(tempDib);
+        OS.DeleteDC(tempHdc);
+    } else {
+        if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+            if (!OS.IsWinCE) OS.SetStretchBltMode(memHdc, OS.COLORONCOLOR);
+            OS.StretchBlt(memHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
+        } else {
+            OS.BitBlt(memHdc, 0, 0, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY);
+        }
+    }
+    OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes);
+
+    /* Compose the pixels */
+    final int dpinc = dibBM.bmWidthBytes - destWidth * 4;
+    int dp = 0;
+    for (int y = 0; y < destHeight; ++y) {
+        for (int x = 0; x < destWidth; ++x) {
+            if (hasAlphaChannel) alpha = srcData[dp + 3] & 0xff;
+            destData[dp] += ((srcData[dp] & 0xff) - (destData[dp] & 0xff)) * alpha / 255;
+            destData[dp + 1] += ((srcData[dp + 1] & 0xff) - (destData[dp + 1] & 0xff)) * alpha / 255;
+            destData[dp + 2] += ((srcData[dp + 2] & 0xff) - (destData[dp + 2] & 0xff)) * alpha / 255;
+            dp += 4;
+        }
+        dp += dpinc;
+    }
+
+    /* Draw the composed pixels */
+    OS.MoveMemory(dibBM.bmBits, destData, sizeInBytes);
+    OS.BitBlt(handle, destX, destY, destWidth, destHeight, memHdc, 0, 0, OS.SRCCOPY);
+
+    /* Free resources */
+    OS.SelectObject(memHdc, oldMemBitmap);
+    OS.DeleteDC(memHdc);
+    OS.DeleteObject(memDib);
+    OS.SelectObject(srcHdc, oldSrcBitmap);
+    OS.DeleteDC(srcHdc);
+}
+
+void drawBitmapTransparentByClipping(int srcHdc, int maskHdc, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, int imgWidth, int imgHeight) {
+    /* Create a clipping region from the mask */
+    int rgn = OS.CreateRectRgn(0, 0, 0, 0);
+    for (int y=0; y<imgHeight; y++) {
+        for (int x=0; x<imgWidth; x++) {
+            if (OS.GetPixel(maskHdc, x, y) is 0) {
+                int tempRgn = OS.CreateRectRgn(x, y, x+1, y+1);
+                OS.CombineRgn(rgn, rgn, tempRgn, OS.RGN_OR);
+                OS.DeleteObject(tempRgn);
+            }
+        }
+    }
+    /* Stretch the clipping mask if needed */
+    if (destWidth !is srcWidth || destHeight !is srcHeight) {
+        int nBytes = OS.GetRegionData (rgn, 0, null);
+        int[] lpRgnData = new int[nBytes / 4];
+        OS.GetRegionData (rgn, nBytes, lpRgnData);
+        float[] lpXform = new float[] {(float)destWidth/srcWidth, 0, 0, (float)destHeight/srcHeight, 0, 0};
+        int tmpRgn = OS.ExtCreateRegion(lpXform, nBytes, lpRgnData);
+        OS.DeleteObject(rgn);
+        rgn = tmpRgn;
+    }
+    OS.OffsetRgn(rgn, destX, destY);
+    int clip = OS.CreateRectRgn(0, 0, 0, 0);
+    int result = OS.GetClipRgn(handle, clip);
+    if (result is 1) OS.CombineRgn(rgn, rgn, clip, OS.RGN_AND);
+    OS.SelectClipRgn(handle, rgn);
+    int rop2 = 0;
+    if (!OS.IsWinCE) {
+        rop2 = OS.GetROP2(handle);
+    } else {
+        rop2 = OS.SetROP2 (handle, OS.R2_COPYPEN);
+        OS.SetROP2 (handle, rop2);
+    }
+    int dwRop = rop2 is OS.R2_XORPEN ? OS.SRCINVERT : OS.SRCCOPY;
+    if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+        int mode = 0;
+        if (!OS.IsWinCE) mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR);
+        OS.StretchBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, dwRop);
+        if (!OS.IsWinCE) OS.SetStretchBltMode(handle, mode);
+    } else {
+        OS.BitBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, dwRop);
+    }
+    OS.SelectClipRgn(handle, result is 1 ? clip : 0);
+    OS.DeleteObject(clip);
+    OS.DeleteObject(rgn);
+}
+
+void drawBitmapMask(Image srcImage, int srcColor, int srcMask, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, int imgWidth, int imgHeight, bool offscreen) {
+    int srcColorY = srcY;
+    if (srcColor is 0) {
+        srcColor = srcMask;
+        srcColorY += imgHeight;
+    }
+    int srcHdc = OS.CreateCompatibleDC(handle);
+    int oldSrcBitmap = OS.SelectObject(srcHdc, srcColor);
+    int destHdc = handle, x = destX, y = destY;
+    int tempHdc = 0, tempBitmap = 0, oldTempBitmap = 0;
+    int oldBkColor = 0, oldTextColor = 0;
+    if (offscreen) {
+        tempHdc = OS.CreateCompatibleDC(handle);
+        tempBitmap = OS.CreateCompatibleBitmap(handle, destWidth, destHeight);
+        oldTempBitmap = OS.SelectObject(tempHdc, tempBitmap);
+        OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, handle, destX, destY, OS.SRCCOPY);
+        destHdc = tempHdc;
+        x = y = 0;
+    } else {
+        oldBkColor = OS.SetBkColor(handle, 0xFFFFFF);
+        oldTextColor = OS.SetTextColor(handle, 0);
+    }
+    if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+        int mode = 0;
+        if (!OS.IsWinCE) mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR);
+        OS.StretchBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, srcWidth, srcHeight, OS.SRCINVERT);
+        OS.SelectObject(srcHdc, srcMask);
+        OS.StretchBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCAND);
+        OS.SelectObject(srcHdc, srcColor);
+        OS.StretchBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, srcWidth, srcHeight, OS.SRCINVERT);
+        if (!OS.IsWinCE) OS.SetStretchBltMode(handle, mode);
+    } else {
+        OS.BitBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, OS.SRCINVERT);
+        OS.SetTextColor(destHdc, 0);
+        OS.SelectObject(srcHdc, srcMask);
+        OS.BitBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCAND);
+        OS.SelectObject(srcHdc, srcColor);
+        OS.BitBlt(destHdc, x, y, destWidth, destHeight, srcHdc, srcX, srcColorY, OS.SRCINVERT);
+    }
+    if (offscreen) {
+        OS.BitBlt(handle, destX, destY, destWidth, destHeight, tempHdc, 0, 0, OS.SRCCOPY);
+        OS.SelectObject(tempHdc, oldTempBitmap);
+        OS.DeleteDC(tempHdc);
+        OS.DeleteObject(tempBitmap);
+    } else {
+        OS.SetBkColor(handle, oldBkColor);
+        OS.SetTextColor(handle, oldTextColor);
+    }
+    OS.SelectObject(srcHdc, oldSrcBitmap);
+    OS.DeleteDC(srcHdc);
+}
+
+void drawBitmapTransparent(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, BITMAP bm, int imgWidth, int imgHeight) {
+
+    /* Find the RGB values for the transparent pixel. */
+    int transBlue = 0, transGreen = 0, transRed = 0;
+    bool isDib = bm.bmBits !is 0;
+    int hBitmap = srcImage.handle;
+    int srcHdc = OS.CreateCompatibleDC(handle);
+    int oldSrcBitmap = OS.SelectObject(srcHdc, hBitmap);
+    byte[] originalColors = null;
+    if (bm.bmBitsPixel <= 8) {
+        if (isDib) {
+            /* Palette-based DIBSECTION */
+            if (OS.IsWinCE) {
+                byte[] pBits = new byte[1];
+                OS.MoveMemory(pBits, bm.bmBits, 1);
+                byte oldValue = pBits[0];
+                int mask = (0xFF << (8 - bm.bmBitsPixel)) & 0x00FF;
+                pBits[0] = (byte)((srcImage.transparentPixel << (8 - bm.bmBitsPixel)) | (pBits[0] & ~mask));
+                OS.MoveMemory(bm.bmBits, pBits, 1);
+                int color = OS.GetPixel(srcHdc, 0, 0);
+                pBits[0] = oldValue;
+                OS.MoveMemory(bm.bmBits, pBits, 1);
+                transBlue = (color & 0xFF0000) >> 16;
+                transGreen = (color & 0xFF00) >> 8;
+                transRed = color & 0xFF;
+            } else {
+                int maxColors = 1 << bm.bmBitsPixel;
+                byte[] oldColors = new byte[maxColors * 4];
+                OS.GetDIBColorTable(srcHdc, 0, maxColors, oldColors);
+                int offset = srcImage.transparentPixel * 4;
+                bool fixPalette = false;
+                for (int i = 0; i < oldColors.length; i += 4) {
+                    if (i !is offset) {
+                        if (oldColors[offset] is oldColors[i] && oldColors[offset+1] is oldColors[i+1] && oldColors[offset+2] is oldColors[i+2]) {
+                            fixPalette = true;
+                            break;
+                        }
+                    }
+                }
+                if (fixPalette) {
+                    byte[] newColors = new byte[oldColors.length];
+                    transRed = transGreen = transBlue = 0xff;
+                    newColors[offset] = (byte)transBlue;
+                    newColors[offset+1] = (byte)transGreen;
+                    newColors[offset+2] = (byte)transRed;
+                    OS.SetDIBColorTable(srcHdc, 0, maxColors, newColors);
+                    originalColors = oldColors;
+                } else {
+                    transBlue = oldColors[offset] & 0xFF;
+                    transGreen = oldColors[offset+1] & 0xFF;
+                    transRed = oldColors[offset+2] & 0xFF;
+                }
+            }
+        } else {
+            /* Palette-based bitmap */
+            int numColors = 1 << bm.bmBitsPixel;
+            /* Set the few fields necessary to get the RGB data out */
+            BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
+            bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+            bmiHeader.biPlanes = bm.bmPlanes;
+            bmiHeader.biBitCount = bm.bmBitsPixel;
+            byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + numColors * 4];
+            OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+            if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+            OS.GetDIBits(srcHdc, srcImage.handle, 0, 0, 0, bmi, OS.DIB_RGB_COLORS);
+            int offset = BITMAPINFOHEADER.sizeof + 4 * srcImage.transparentPixel;
+            transRed = bmi[offset + 2] & 0xFF;
+            transGreen = bmi[offset + 1] & 0xFF;
+            transBlue = bmi[offset] & 0xFF;
+        }
+    } else {
+        /* Direct color image */
+        int pixel = srcImage.transparentPixel;
+        switch (bm.bmBitsPixel) {
+            case 16:
+                transBlue = (pixel & 0x1F) << 3;
+                transGreen = (pixel & 0x3E0) >> 2;
+                transRed = (pixel & 0x7C00) >> 7;
+                break;
+            case 24:
+                transBlue = (pixel & 0xFF0000) >> 16;
+                transGreen = (pixel & 0xFF00) >> 8;
+                transRed = pixel & 0xFF;
+                break;
+            case 32:
+                transBlue = (pixel & 0xFF000000) >>> 24;
+                transGreen = (pixel & 0xFF0000) >> 16;
+                transRed = (pixel & 0xFF00) >> 8;
+                break;
+        }
+    }
+
+    int transparentColor = transBlue << 16 | transGreen << 8 | transRed;
+    if (OS.IsWinCE) {
+        /*
+        * Note in WinCE. TransparentImage uses the first entry of a palette
+        * based image when there are multiple entries that have the same
+        * transparent color.
+        */
+        OS.TransparentImage(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, transparentColor);
+    } else if (originalColors is null && OS.IsWinNT && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
+        int mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR);
+        OS.TransparentBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, transparentColor);
+        OS.SetStretchBltMode(handle, mode);
+    } else {
+        /* Create the mask for the source image */
+        int maskHdc = OS.CreateCompatibleDC(handle);
+        int maskBitmap = OS.CreateBitmap(imgWidth, imgHeight, 1, 1, null);
+        int oldMaskBitmap = OS.SelectObject(maskHdc, maskBitmap);
+        OS.SetBkColor(srcHdc, transparentColor);
+        OS.BitBlt(maskHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY);
+        if (originalColors !is null) OS.SetDIBColorTable(srcHdc, 0, 1 << bm.bmBitsPixel, originalColors);
+
+        if (OS.GetDeviceCaps(handle, OS.TECHNOLOGY) is OS.DT_RASPRINTER) {
+            /* Most printers do not support BitBlt(), draw the source bitmap transparently using clipping */
+            drawBitmapTransparentByClipping(srcHdc, maskHdc, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, imgWidth, imgHeight);
+        } else {
+            /* Draw the source bitmap transparently using invert/and mask/invert */
+            int tempHdc = OS.CreateCompatibleDC(handle);
+            int tempBitmap = OS.CreateCompatibleBitmap(handle, destWidth, destHeight);
+            int oldTempBitmap = OS.SelectObject(tempHdc, tempBitmap);
+            OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, handle, destX, destY, OS.SRCCOPY);
+            if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+                if (!OS.IsWinCE) OS.SetStretchBltMode(tempHdc, OS.COLORONCOLOR);
+                OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCINVERT);
+                OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, maskHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCAND);
+                OS.StretchBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, OS.SRCINVERT);
+            } else {
+                OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCINVERT);
+                OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, maskHdc, srcX, srcY, OS.SRCAND);
+                OS.BitBlt(tempHdc, 0, 0, destWidth, destHeight, srcHdc, srcX, srcY, OS.SRCINVERT);
+            }
+            OS.BitBlt(handle, destX, destY, destWidth, destHeight, tempHdc, 0, 0, OS.SRCCOPY);
+            OS.SelectObject(tempHdc, oldTempBitmap);
+            OS.DeleteDC(tempHdc);
+            OS.DeleteObject(tempBitmap);
+        }
+        OS.SelectObject(maskHdc, oldMaskBitmap);
+        OS.DeleteDC(maskHdc);
+        OS.DeleteObject(maskBitmap);
+    }
+    OS.SelectObject(srcHdc, oldSrcBitmap);
+    if (hBitmap !is srcImage.handle) OS.DeleteObject(hBitmap);
+    OS.DeleteDC(srcHdc);
+}
+
+void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, bool simple, BITMAP bm, int imgWidth, int imgHeight) {
+    int srcHdc = OS.CreateCompatibleDC(handle);
+    int oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle);
+    int rop2 = 0;
+    if (!OS.IsWinCE) {
+        rop2 = OS.GetROP2(handle);
+    } else {
+        rop2 = OS.SetROP2 (handle, OS.R2_COPYPEN);
+        OS.SetROP2 (handle, rop2);
+    }
+    int dwRop = rop2 is OS.R2_XORPEN ? OS.SRCINVERT : OS.SRCCOPY;
+    if (!simple && (srcWidth !is destWidth || srcHeight !is destHeight)) {
+        int mode = 0;
+        if (!OS.IsWinCE) mode = OS.SetStretchBltMode(handle, OS.COLORONCOLOR);
+        OS.StretchBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, dwRop);
+        if (!OS.IsWinCE) OS.SetStretchBltMode(handle, mode);
+    } else {
+        OS.BitBlt(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, dwRop);
+    }
+    OS.SelectObject(srcHdc, oldSrcBitmap);
+    OS.DeleteDC(srcHdc);
+}
+
+/**
+ * Draws a line, using the foreground color, between the points
+ * (<code>x1</code>, <code>y1</code>) and (<code>x2</code>, <code>y2</code>).
+ *
+ * @param x1 the first point's x coordinate
+ * @param y1 the first point's y coordinate
+ * @param x2 the second point's x coordinate
+ * @param y2 the second point's y coordinate
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawLine (int x1, int y1, int x2, int y2) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(DRAW);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        Gdip.Graphics_DrawLine(gdipGraphics, data.gdipPen, x1, y1, x2, y2);
+        Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+            x1--;
+            x2--;
+        }
+    }
+    if (OS.IsWinCE) {
+        int [] points = new int [] {x1, y1, x2, y2};
+        OS.Polyline (handle, points, points.length / 2);
+    } else {
+        OS.MoveToEx (handle, x1, y1, 0);
+        OS.LineTo (handle, x2, y2);
+    }
+    if (data.lineWidth <= 1) {
+        OS.SetPixel (handle, x2, y2, data.foreground);
+    }
+}
+
+/**
+ * Draws the outline of an oval, using the foreground color,
+ * within the specified rectangular area.
+ * <p>
+ * The result is a circle or ellipse that fits within the
+ * rectangle specified by the <code>x</code>, <code>y</code>,
+ * <code>width</code>, and <code>height</code> arguments.
+ * </p><p>
+ * The oval covers an area that is <code>width + 1</code>
+ * pixels wide and <code>height + 1</code> pixels tall.
+ * </p>
+ *
+ * @param x the x coordinate of the upper left corner of the oval to be drawn
+ * @param y the y coordinate of the upper left corner of the oval to be drawn
+ * @param width the width of the oval to be drawn
+ * @param height the height of the oval to be drawn
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawOval (int x, int y, int width, int height) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(DRAW);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        Gdip.Graphics_DrawEllipse(gdipGraphics, data.gdipPen, x, y, width, height);
+        Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) x--;
+    }
+    OS.Ellipse(handle, x, y, x + width + 1, y + height + 1);
+}
+
+/**
+ * Draws the path described by the parameter.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param path the path to draw
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see Path
+ *
+ * @since 3.1
+ */
+public void drawPath (Path path) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (path is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (path.handle is 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    initGdip();
+    checkGC(DRAW);
+    int gdipGraphics = data.gdipGraphics;
+    Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+    Gdip.Graphics_DrawPath(gdipGraphics, data.gdipPen, path.handle);
+    Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+}
+
+/**
+ * Draws a pixel, using the foreground color, at the specified
+ * point (<code>x</code>, <code>y</code>).
+ * <p>
+ * Note that the receiver's line attributes do not affect this
+ * operation.
+ * </p>
+ *
+ * @param x the point's x coordinate
+ * @param y the point's y coordinate
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void drawPoint (int x, int y) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics !is 0) {
+        checkGC(DRAW);
+        Gdip.Graphics_FillRectangle(data.gdipGraphics, getFgBrush(), x, y, 1, 1);
+        return;
+    }
+    OS.SetPixel (handle, x, y, data.foreground);
+}
+
+/**
+ * Draws the closed polygon which is defined by the specified array
+ * of integer coordinates, using the receiver's foreground color. The array
+ * contains alternating x and y values which are considered to represent
+ * points which are the vertices of the polygon. Lines are drawn between
+ * each consecutive pair, and between the first pair and last pair in the
+ * array.
+ *
+ * @param pointArray an array of alternating x and y values which are the vertices of the polygon
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT if pointArray is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawPolygon(int[] pointArray) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (pointArray is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    checkGC(DRAW);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        Gdip.Graphics_DrawPolygon(gdipGraphics, data.gdipPen, pointArray, pointArray.length / 2);
+        Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+            for (int i = 0; i < pointArray.length; i+=2) {
+                pointArray[i]--;
+            }
+        }
+    }
+    OS.Polygon(handle, pointArray, pointArray.length / 2);
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+            for (int i = 0; i < pointArray.length; i+=2) {
+                pointArray[i]++;
+            }
+        }
+    }
+}
+
+/**
+ * Draws the polyline which is defined by the specified array
+ * of integer coordinates, using the receiver's foreground color. The array
+ * contains alternating x and y values which are considered to represent
+ * points which are the corners of the polyline. Lines are drawn between
+ * each consecutive pair, but not between the first pair and last pair in
+ * the array.
+ *
+ * @param pointArray an array of alternating x and y values which are the corners of the polyline
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point array is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawPolyline(int[] pointArray) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (pointArray is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    checkGC(DRAW);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        Gdip.Graphics_DrawLines(gdipGraphics, data.gdipPen, pointArray, pointArray.length / 2);
+        Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+            for (int i = 0; i < pointArray.length; i+=2) {
+                pointArray[i]--;
+            }
+        }
+    }
+    OS.Polyline(handle, pointArray, pointArray.length / 2);
+    int length = pointArray.length;
+    if (length >= 2) {
+        if (data.lineWidth <= 1) {
+            OS.SetPixel (handle, pointArray[length - 2], pointArray[length - 1], data.foreground);
+        }
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) {
+            for (int i = 0; i < pointArray.length; i+=2) {
+                pointArray[i]++;
+            }
+        }
+    }
+}
+
+/**
+ * Draws the outline of the rectangle specified by the arguments,
+ * using the receiver's foreground color. The left and right edges
+ * of the rectangle are at <code>x</code> and <code>x + width</code>.
+ * The top and bottom edges are at <code>y</code> and <code>y + height</code>.
+ *
+ * @param x the x coordinate of the rectangle to be drawn
+ * @param y the y coordinate of the rectangle to be drawn
+ * @param width the width of the rectangle to be drawn
+ * @param height the height of the rectangle to be drawn
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawRectangle (int x, int y, int width, int height) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(DRAW);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        Gdip.Graphics_DrawRectangle(gdipGraphics, data.gdipPen, x, y, width, height);
+        Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        /*
+        * Note that Rectangle() subtracts one pixel in MIRRORED mode when
+        * the pen was created with CreatePen() and its width is 0 or 1.
+        */
+        if (data.lineWidth > 1) {
+            if ((data.lineWidth % 2) is 1) x++;
+        } else {
+            if (data.hPen !is 0 && OS.GetObject(data.hPen, 0, 0) !is LOGPEN.sizeof) {
+                x++;
+            }
+        }
+    }
+    OS.Rectangle (handle, x, y, x + width + 1, y + height + 1);
+}
+
+/**
+ * Draws the outline of the specified rectangle, using the receiver's
+ * foreground color. The left and right edges of the rectangle are at
+ * <code>rect.x</code> and <code>rect.x + rect.width</code>. The top
+ * and bottom edges are at <code>rect.y</code> and
+ * <code>rect.y + rect.height</code>.
+ *
+ * @param rect the rectangle to draw
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawRectangle (Rectangle rect) {
+    if (rect is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    drawRectangle (rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * Draws the outline of the round-cornered rectangle specified by
+ * the arguments, using the receiver's foreground color. The left and
+ * right edges of the rectangle are at <code>x</code> and <code>x + width</code>.
+ * The top and bottom edges are at <code>y</code> and <code>y + height</code>.
+ * The <em>roundness</em> of the corners is specified by the
+ * <code>arcWidth</code> and <code>arcHeight</code> arguments, which
+ * are respectively the width and height of the ellipse used to draw
+ * the corners.
+ *
+ * @param x the x coordinate of the rectangle to be drawn
+ * @param y the y coordinate of the rectangle to be drawn
+ * @param width the width of the rectangle to be drawn
+ * @param height the height of the rectangle to be drawn
+ * @param arcWidth the width of the arc
+ * @param arcHeight the height of the arc
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawRoundRectangle (int x, int y, int width, int height, int arcWidth, int arcHeight) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(DRAW);
+    if (data.gdipGraphics !is 0) {
+        drawRoundRectangleGdip(data.gdipGraphics, data.gdipPen, x, y, width, height, arcWidth, arcHeight);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (data.lineWidth !is 0 && data.lineWidth % 2 is 0) x--;
+    }
+    if (OS.IsWinCE) {
+        /*
+        * Bug in WinCE PPC.  On certain devices, RoundRect does not draw
+        * all the pixels.  The workaround is to draw a round rectangle
+        * using lines and arcs.
+        */
+        if (width is 0 || height is 0) return;
+        if (arcWidth is 0 || arcHeight is 0) {
+            drawRectangle(x, y, width, height);
+            return;
+        }
+        if (width < 0) {
+            x += width;
+            width = -width;
+        }
+        if (height < 0) {
+            y += height;
+            height = -height;
+        }
+        if (arcWidth < 0) arcWidth = -arcWidth;
+        if (arcHeight < 0) arcHeight = -arcHeight;
+        if (arcWidth > width) arcWidth = width;
+        if (arcHeight > height) arcHeight = height;
+
+        if (arcWidth < width) {
+            drawLine(x+arcWidth/2, y, x+width-arcWidth/2, y);
+            drawLine(x+arcWidth/2, y+height, x+width-arcWidth/2, y+height);
+        }
+        if (arcHeight < height) {
+            drawLine(x, y+arcHeight/2, x, y+height-arcHeight/2);
+            drawLine(x+width, y+arcHeight/2, x+width, y+height-arcHeight/2);
+        }
+        if (arcWidth !is 0 && arcHeight !is 0) {
+            drawArc(x, y, arcWidth, arcHeight, 90, 90);
+            drawArc(x+width-arcWidth, y, arcWidth, arcHeight, 0, 90);
+            drawArc(x+width-arcWidth, y+height-arcHeight, arcWidth, arcHeight, 0, -90);
+            drawArc(x, y+height-arcHeight, arcWidth, arcHeight, 180, 90);
+        }
+    } else {
+        OS.RoundRect(handle, x,y,x+width+1,y+height+1, arcWidth, arcHeight);
+    }
+}
+
+void drawRoundRectangleGdip (int gdipGraphics, int pen, int x, int y, int width, int height, int arcWidth, int arcHeight) {
+    int nx = x;
+    int ny = y;
+    int nw = width;
+    int nh = height;
+    int naw = arcWidth;
+    int nah = arcHeight;
+
+    if (nw < 0) {
+        nw = 0 - nw;
+        nx = nx - nw;
+    }
+    if (nh < 0) {
+        nh = 0 - nh;
+        ny = ny - nh;
+    }
+    if (naw < 0)
+        naw = 0 - naw;
+    if (nah < 0)
+        nah = 0 - nah;
+
+    Gdip.Graphics_TranslateTransform(gdipGraphics, data.gdipXOffset, data.gdipYOffset, Gdip.MatrixOrderPrepend);
+    int path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate);
+    if (path is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    if (nw > naw) {
+        if (nh > nah) {
+            Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nah, 0, -90);
+            Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nah, -90, -90);
+            Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, naw, nah, -180, -90);
+            Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny + nh - nah, naw, nah, -270, -90);
+        } else {
+            Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nh, -270, -180);
+            Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nh, -90, -180);
+        }
+    } else {
+        if (nh > nah) {
+            Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nah, 0, -180);
+            Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, nw, nah, -180, -180);
+        } else {
+            Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nh, 0, 360);
+        }
+    }
+    Gdip.GraphicsPath_CloseFigure(path);
+    Gdip.Graphics_DrawPath(gdipGraphics, pen, path);
+    Gdip.GraphicsPath_delete(path);
+    Gdip.Graphics_TranslateTransform(gdipGraphics, -data.gdipXOffset, -data.gdipYOffset, Gdip.MatrixOrderPrepend);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. No tab expansion or carriage return processing
+ * will be performed. The background of the rectangular area where
+ * the string is being drawn will be filled with the receiver's
+ * background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the string is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the string is to be drawn
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawString (String string, int x, int y) {
+    drawString(string, x, y, false);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. No tab expansion or carriage return processing
+ * will be performed. If <code>isTransparent</code> is <code>true</code>,
+ * then the background of the rectangular area where the string is being
+ * drawn will not be modified, otherwise it will be filled with the
+ * receiver's background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the string is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the string is to be drawn
+ * @param isTransparent if <code>true</code> the background will be transparent, otherwise it will be opaque
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawString (String string, int x, int y, bool isTransparent) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (string is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+//  TCHAR buffer = new TCHAR (getCodePage(), string, false);
+    int length = string.length();
+    if (length is 0) return;
+    char[] buffer = new char [length];
+    string.getChars(0, length, buffer, 0);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        checkGC(FONT | FOREGROUND | (isTransparent ? 0 : BACKGROUND));
+        PointF pt = new PointF();
+        int format = Gdip.StringFormat_Clone(Gdip.StringFormat_GenericTypographic());
+        int formatFlags = Gdip.StringFormat_GetFormatFlags(format) | Gdip.StringFormatFlagsMeasureTrailingSpaces;
+        if ((data.style & DWT.MIRRORED) !is 0) formatFlags |= Gdip.StringFormatFlagsDirectionRightToLeft;
+        Gdip.StringFormat_SetFormatFlags(format, formatFlags);
+        if (!isTransparent) {
+            RectF bounds = new RectF();
+            Gdip.Graphics_MeasureString(gdipGraphics, buffer, length, data.gdipFont, pt, format, bounds);
+            Gdip.Graphics_FillRectangle(gdipGraphics, data.gdipBrush, x, y, Math.round(bounds.Width), Math.round(bounds.Height));
+        }
+        int gstate = 0, brush = getFgBrush();
+        if ((data.style & DWT.MIRRORED) !is 0) {
+            switch (Gdip.Brush_GetType(brush)) {
+                case Gdip.BrushTypeLinearGradient:
+                    Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+                    Gdip.LinearGradientBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+                    break;
+                case Gdip.BrushTypeTextureFill:
+                    Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+                    Gdip.TextureBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+                    break;
+            }
+            gstate = Gdip.Graphics_Save(gdipGraphics);
+            Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend);
+            Gdip.Graphics_TranslateTransform(gdipGraphics, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+        }
+        pt.X = x;
+        pt.Y = y;
+        Gdip.Graphics_DrawString(gdipGraphics, buffer, length, data.gdipFont, pt, format, brush);
+        if ((data.style & DWT.MIRRORED) !is 0) {
+            switch (Gdip.Brush_GetType(brush)) {
+                case Gdip.BrushTypeLinearGradient:
+                    Gdip.LinearGradientBrush_ResetTransform(brush);
+                    break;
+                case Gdip.BrushTypeTextureFill:
+                    Gdip.TextureBrush_ResetTransform(brush);
+                    break;
+            }
+            Gdip.Graphics_Restore(gdipGraphics, gstate);
+        }
+        Gdip.StringFormat_delete(format);
+        return;
+    }
+    int rop2 = 0;
+    if (OS.IsWinCE) {
+        rop2 = OS.SetROP2(handle, OS.R2_COPYPEN);
+        OS.SetROP2(handle, rop2);
+    } else {
+        rop2 = OS.GetROP2(handle);
+    }
+    checkGC(FONT | FOREGROUND_TEXT | BACKGROUND_TEXT);
+    int oldBkMode = OS.SetBkMode(handle, isTransparent ? OS.TRANSPARENT : OS.OPAQUE);
+    RECT rect = null;
+    SIZE size = null;
+    int flags = 0;
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        if (!isTransparent) {
+            size = new SIZE();
+            OS.GetTextExtentPoint32W(handle, buffer, length, size);
+            rect = new RECT ();
+            rect.left = x;
+            rect.right = x + size.cx;
+            rect.top = y;
+            rect.bottom = y + size.cy;
+            flags = OS.ETO_CLIPPED;
+        }
+        x--;
+    }
+    if (rop2 !is OS.R2_XORPEN) {
+        OS.ExtTextOutW(handle, x, y, flags, rect, buffer, length, null);
+    } else {
+        int foreground = OS.GetTextColor(handle);
+        if (isTransparent) {
+            if (size is null) {
+                size = new SIZE();
+                OS.GetTextExtentPoint32W(handle, buffer, length, size);
+            }
+            int width = size.cx, height = size.cy;
+            int hBitmap = OS.CreateCompatibleBitmap(handle, width, height);
+            if (hBitmap is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            int memDC = OS.CreateCompatibleDC(handle);
+            int hOldBitmap = OS.SelectObject(memDC, hBitmap);
+            OS.PatBlt(memDC, 0, 0, width, height, OS.BLACKNESS);
+            OS.SetBkMode(memDC, OS.TRANSPARENT);
+            OS.SetTextColor(memDC, foreground);
+            OS.SelectObject(memDC, OS.GetCurrentObject(handle, OS.OBJ_FONT));
+            OS.ExtTextOutW(memDC, 0, 0, 0, null, buffer, length, null);
+            OS.BitBlt(handle, x, y, width, height, memDC, 0, 0, OS.SRCINVERT);
+            OS.SelectObject(memDC, hOldBitmap);
+            OS.DeleteDC(memDC);
+            OS.DeleteObject(hBitmap);
+        } else {
+            int background = OS.GetBkColor(handle);
+            OS.SetTextColor(handle, foreground ^ background);
+            OS.ExtTextOutW(handle, x, y, flags, rect, buffer, length, null);
+            OS.SetTextColor(handle, foreground);
+        }
+    }
+    OS.SetBkMode(handle, oldBkMode);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. Tab expansion and carriage return processing
+ * are performed. The background of the rectangular area where
+ * the text is being drawn will be filled with the receiver's
+ * background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawText (String string, int x, int y) {
+    drawText(string, x, y, DWT.DRAW_DELIMITER | DWT.DRAW_TAB);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. Tab expansion and carriage return processing
+ * are performed. If <code>isTransparent</code> is <code>true</code>,
+ * then the background of the rectangular area where the text is being
+ * drawn will not be modified, otherwise it will be filled with the
+ * receiver's background color.
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param isTransparent if <code>true</code> the background will be transparent, otherwise it will be opaque
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawText (String string, int x, int y, bool isTransparent) {
+    int flags = DWT.DRAW_DELIMITER | DWT.DRAW_TAB;
+    if (isTransparent) flags |= DWT.DRAW_TRANSPARENT;
+    drawText(string, x, y, flags);
+}
+
+/**
+ * Draws the given string, using the receiver's current font and
+ * foreground color. Tab expansion, line delimiter and mnemonic
+ * processing are performed according to the specified flags. If
+ * <code>flags</code> includes <code>DRAW_TRANSPARENT</code>,
+ * then the background of the rectangular area where the text is being
+ * drawn will not be modified, otherwise it will be filled with the
+ * receiver's background color.
+ * <p>
+ * The parameter <code>flags</code> may be a combination of:
+ * <dl>
+ * <dt><b>DRAW_DELIMITER</b></dt>
+ * <dd>draw multiple lines</dd>
+ * <dt><b>DRAW_TAB</b></dt>
+ * <dd>expand tabs</dd>
+ * <dt><b>DRAW_MNEMONIC</b></dt>
+ * <dd>underline the mnemonic character</dd>
+ * <dt><b>DRAW_TRANSPARENT</b></dt>
+ * <dd>transparent background</dd>
+ * </dl>
+ * </p>
+ *
+ * @param string the string to be drawn
+ * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn
+ * @param flags the flags specifying how to process the text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void drawText (String string, int x, int y, int flags) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (string is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (string.length() is 0) return;
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        checkGC(FONT | FOREGROUND | ((flags & DWT.DRAW_TRANSPARENT) !is 0 ? 0 : BACKGROUND));
+        int length = string.length();
+        char[] buffer = new char [length];
+        string.getChars(0, length, buffer, 0);
+        PointF pt = new PointF();
+        int format = Gdip.StringFormat_Clone(Gdip.StringFormat_GenericTypographic());
+        int formatFlags = Gdip.StringFormat_GetFormatFlags(format) | Gdip.StringFormatFlagsMeasureTrailingSpaces;
+        if ((data.style & DWT.MIRRORED) !is 0) formatFlags |= Gdip.StringFormatFlagsDirectionRightToLeft;
+        Gdip.StringFormat_SetFormatFlags(format, formatFlags);
+        float[] tabs = (flags & DWT.DRAW_TAB) !is 0 ? new float[]{measureSpace(data.gdipFont, format) * 8} : new float[1];
+        Gdip.StringFormat_SetTabStops(format, 0, tabs.length, tabs);
+        int hotkeyPrefix = (flags & DWT.DRAW_MNEMONIC) !is 0 ? Gdip.HotkeyPrefixShow : Gdip.HotkeyPrefixNone;
+        if ((flags & DWT.DRAW_MNEMONIC) !is 0 && (data.uiState & OS.UISF_HIDEACCEL) !is 0) hotkeyPrefix = Gdip.HotkeyPrefixHide;
+        Gdip.StringFormat_SetHotkeyPrefix(format, hotkeyPrefix);
+        if ((flags & DWT.DRAW_TRANSPARENT) is 0) {
+            RectF bounds = new RectF();
+            Gdip.Graphics_MeasureString(gdipGraphics, buffer, length, data.gdipFont, pt, format, bounds);
+            Gdip.Graphics_FillRectangle(gdipGraphics, data.gdipBrush, x, y, Math.round(bounds.Width), Math.round(bounds.Height));
+        }
+        int gstate = 0, brush = getFgBrush();
+        if ((data.style & DWT.MIRRORED) !is 0) {
+            switch (Gdip.Brush_GetType(brush)) {
+                case Gdip.BrushTypeLinearGradient:
+                    Gdip.LinearGradientBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+                    Gdip.LinearGradientBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+                    break;
+                case Gdip.BrushTypeTextureFill:
+                    Gdip.TextureBrush_ScaleTransform(brush, -1, 1, Gdip.MatrixOrderPrepend);
+                    Gdip.TextureBrush_TranslateTransform(brush, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+                    break;
+            }
+            gstate = Gdip.Graphics_Save(gdipGraphics);
+            Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend);
+            Gdip.Graphics_TranslateTransform(gdipGraphics, - 2 * x, 0, Gdip.MatrixOrderPrepend);
+        }
+        pt.X = x;
+        pt.Y = y;
+        Gdip.Graphics_DrawString(gdipGraphics, buffer, length, data.gdipFont, pt, format, brush);
+        if ((data.style & DWT.MIRRORED) !is 0) {
+            switch (Gdip.Brush_GetType(brush)) {
+                case Gdip.BrushTypeLinearGradient:
+                    Gdip.LinearGradientBrush_ResetTransform(brush);
+                    break;
+                case Gdip.BrushTypeTextureFill:
+                    Gdip.TextureBrush_ResetTransform(brush);
+                    break;
+            }
+            Gdip.Graphics_Restore(gdipGraphics, gstate);
+        }
+        Gdip.StringFormat_delete(format);
+        return;
+    }
+    TCHAR buffer = new TCHAR(getCodePage(), string, false);
+    int length = buffer.length();
+    if (length is 0) return;
+    RECT rect = new RECT();
+    /*
+    * Feature in Windows.  For some reason DrawText(), the maximum
+    * value for the bottom and right coordinates for the RECT that
+    * is used to position the text is different on between Windows
+    * versions.  If this value is larger than the maximum, nothing
+    * is drawn.  On Windows 98, the limit is 0x7FFF.  On Windows CE,
+    * NT, and 2000 it is 0x6FFFFFF. And on XP, it is 0x7FFFFFFF.
+    * The fix is to use the the smaller limit for Windows 98 and the
+    * larger limit on the other Windows platforms.
+    */
+    int limit = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF;
+    OS.SetRect(rect, x, y, limit, limit);
+    int uFormat = OS.DT_LEFT;
+    if ((flags & DWT.DRAW_DELIMITER) is 0) uFormat |= OS.DT_SINGLELINE;
+    if ((flags & DWT.DRAW_TAB) !is 0) uFormat |= OS.DT_EXPANDTABS;
+    if ((flags & DWT.DRAW_MNEMONIC) is 0) uFormat |= OS.DT_NOPREFIX;
+    if ((flags & DWT.DRAW_MNEMONIC) !is 0 && (data.uiState & OS.UISF_HIDEACCEL) !is 0) {
+        uFormat |= OS.DT_HIDEPREFIX;
+    }
+    int rop2 = 0;
+    if (OS.IsWinCE) {
+        rop2 = OS.SetROP2(handle, OS.R2_COPYPEN);
+        OS.SetROP2(handle, rop2);
+    } else {
+        rop2 = OS.GetROP2(handle);
+    }
+    checkGC(FONT | FOREGROUND_TEXT | BACKGROUND_TEXT);
+    int oldBkMode = OS.SetBkMode(handle, (flags & DWT.DRAW_TRANSPARENT) !is 0 ? OS.TRANSPARENT : OS.OPAQUE);
+    if (rop2 !is OS.R2_XORPEN) {
+        OS.DrawText(handle, buffer, length, rect, uFormat);
+    } else {
+        int foreground = OS.GetTextColor(handle);
+        if ((flags & DWT.DRAW_TRANSPARENT) !is 0) {
+            OS.DrawText(handle, buffer, buffer.length(), rect, uFormat | OS.DT_CALCRECT);
+            int width = rect.right - rect.left;
+            int height = rect.bottom - rect.top;
+            int hBitmap = OS.CreateCompatibleBitmap(handle, width, height);
+            if (hBitmap is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            int memDC = OS.CreateCompatibleDC(handle);
+            int hOldBitmap = OS.SelectObject(memDC, hBitmap);
+            OS.PatBlt(memDC, 0, 0, width, height, OS.BLACKNESS);
+            OS.SetBkMode(memDC, OS.TRANSPARENT);
+            OS.SetTextColor(memDC, foreground);
+            OS.SelectObject(memDC, OS.GetCurrentObject(handle, OS.OBJ_FONT));
+            OS.SetRect(rect, 0, 0, 0x7FFF, 0x7FFF);
+            OS.DrawText(memDC, buffer, length, rect, uFormat);
+            OS.BitBlt(handle, x, y, width, height, memDC, 0, 0, OS.SRCINVERT);
+            OS.SelectObject(memDC, hOldBitmap);
+            OS.DeleteDC(memDC);
+            OS.DeleteObject(hBitmap);
+        } else {
+            int background = OS.GetBkColor(handle);
+            OS.SetTextColor(handle, foreground ^ background);
+            OS.DrawText(handle, buffer, length, rect, uFormat);
+            OS.SetTextColor(handle, foreground);
+        }
+    }
+    OS.SetBkMode(handle, oldBkMode);
+}
+
+/**
+ * Compares the argument to the receiver, and returns true
+ * if they represent the <em>same</em> object using a class
+ * specific comparison.
+ *
+ * @param object the object to compare with this object
+ * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
+ *
+ * @see #hashCode
+ */
+public bool equals (Object object) {
+    return (object is this) || ((object instanceof GC) && (handle is ((GC)object).handle));
+}
+
+/**
+ * Fills the interior of a circular or elliptical arc within
+ * the specified rectangular area, with the receiver's background
+ * color.
+ * <p>
+ * The resulting arc begins at <code>startAngle</code> and extends
+ * for <code>arcAngle</code> degrees, using the current color.
+ * Angles are interpreted such that 0 degrees is at the 3 o'clock
+ * position. A positive value indicates a counter-clockwise rotation
+ * while a negative value indicates a clockwise rotation.
+ * </p><p>
+ * The center of the arc is the center of the rectangle whose origin
+ * is (<code>x</code>, <code>y</code>) and whose size is specified by the
+ * <code>width</code> and <code>height</code> arguments.
+ * </p><p>
+ * The resulting arc covers an area <code>width + 1</code> pixels wide
+ * by <code>height + 1</code> pixels tall.
+ * </p>
+ *
+ * @param x the x coordinate of the upper-left corner of the arc to be filled
+ * @param y the y coordinate of the upper-left corner of the arc to be filled
+ * @param width the width of the arc to be filled
+ * @param height the height of the arc to be filled
+ * @param startAngle the beginning angle
+ * @param arcAngle the angular extent of the arc, relative to the start angle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawArc
+ */
+public void fillArc (int x, int y, int width, int height, int startAngle, int arcAngle) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(FILL);
+    if (width < 0) {
+        x = x + width;
+        width = -width;
+    }
+    if (height < 0) {
+        y = y + height;
+        height = -height;
+    }
+    if (width is 0 || height is 0 || arcAngle is 0) return;
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        if (width is height) {
+            Gdip.Graphics_FillPie(gdipGraphics, data.gdipBrush, x, y, width, height, -startAngle, -arcAngle);
+        } else {
+            int state = Gdip.Graphics_Save(gdipGraphics);
+            Gdip.Graphics_TranslateTransform(gdipGraphics, x, y, Gdip.MatrixOrderPrepend);
+            Gdip.Graphics_ScaleTransform(gdipGraphics, width, height, Gdip.MatrixOrderPrepend);
+            Gdip.Graphics_FillPie(gdipGraphics, data.gdipBrush, 0, 0, 1, 1, -startAngle, -arcAngle);
+            Gdip.Graphics_Restore(gdipGraphics, state);
+        }
+        return;
+    }
+
+    if ((data.style & DWT.MIRRORED) !is 0) x--;
+    /*
+    * Feature in WinCE.  The function Pie is not present in the
+    * WinCE SDK.  The fix is to emulate it by using Polygon.
+    */
+    if (OS.IsWinCE) {
+        /* compute arc with a simple linear interpolation */
+        if (arcAngle < 0) {
+            startAngle += arcAngle;
+            arcAngle = -arcAngle;
+        }
+        bool drawSegments = true;
+        if (arcAngle >= 360) {
+            arcAngle = 360;
+            drawSegments = false;
+        }
+        int[] points = new int[(arcAngle + 1) * 2 + (drawSegments ? 4 : 0)];
+        int cteX = 2 * x + width;
+        int cteY = 2 * y + height;
+        int index = (drawSegments ? 2 : 0);
+        for (int i = 0; i <= arcAngle; i++) {
+            points[index++] = (Compatibility.cos(startAngle + i, width) + cteX) >> 1;
+            points[index++] = (cteY - Compatibility.sin(startAngle + i, height)) >> 1;
+        }
+        if (drawSegments) {
+            points[0] = points[points.length - 2] = cteX >> 1;
+            points[1] = points[points.length - 1] = cteY >> 1;
+        }
+        OS.Polygon(handle, points, points.length / 2);
+    } else {
+        int x1, y1, x2, y2,tmp;
+        bool isNegative;
+        if (arcAngle >= 360 || arcAngle <= -360) {
+            x1 = x2 = x + width;
+            y1 = y2 = y + height / 2;
+        } else {
+            isNegative = arcAngle < 0;
+
+            arcAngle = arcAngle + startAngle;
+            if (isNegative) {
+                // swap angles
+                tmp = startAngle;
+                startAngle = arcAngle;
+                arcAngle = tmp;
+            }
+            x1 = Compatibility.cos(startAngle, width) + x + width/2;
+            y1 = -1 * Compatibility.sin(startAngle, height) + y + height/2;
+
+            x2 = Compatibility.cos(arcAngle, width) + x + width/2;
+            y2 = -1 * Compatibility.sin(arcAngle, height) + y + height/2;
+        }
+        OS.Pie(handle, x, y, x + width + 1, y + height + 1, x1, y1, x2, y2);
+    }
+}
+
+/**
+ * Fills the interior of the specified rectangle with a gradient
+ * sweeping from left to right or top to bottom progressing
+ * from the receiver's foreground color to its background color.
+ *
+ * @param x the x coordinate of the rectangle to be filled
+ * @param y the y coordinate of the rectangle to be filled
+ * @param width the width of the rectangle to be filled, may be negative
+ *        (inverts direction of gradient if horizontal)
+ * @param height the height of the rectangle to be filled, may be negative
+ *        (inverts direction of gradient if vertical)
+ * @param vertical if true sweeps from top to bottom, else
+ *        sweeps from left to right
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawRectangle(int, int, int, int)
+ */
+public void fillGradientRectangle(int x, int y, int width, int height, bool vertical) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (width is 0 || height is 0) return;
+
+    RGB backgroundRGB, foregroundRGB;
+    backgroundRGB = getBackground().getRGB();
+    foregroundRGB = getForeground().getRGB();
+
+    RGB fromRGB, toRGB;
+    fromRGB = foregroundRGB;
+    toRGB   = backgroundRGB;
+
+    bool swapColors = false;
+    if (width < 0) {
+        x += width; width = -width;
+        if (! vertical) swapColors = true;
+    }
+    if (height < 0) {
+        y += height; height = -height;
+        if (vertical) swapColors = true;
+    }
+    if (swapColors) {
+        fromRGB = backgroundRGB;
+        toRGB   = foregroundRGB;
+    }
+    if (fromRGB.equals(toRGB)) {
+        fillRectangle(x, y, width, height);
+        return;
+    }
+    if (data.gdipGraphics !is 0) {
+        initGdip();
+        PointF p1= new PointF(), p2 = new PointF();
+        p1.X = x;
+        p1.Y = y;
+        if (vertical) {
+            p2.X = p1.X;
+            p2.Y = p1.Y + height;
+        } else {
+            p2.X = p1.X + width;
+            p2.Y = p1.Y;
+        }
+        int rgb = ((fromRGB.red & 0xFF) << 16) | ((fromRGB.green & 0xFF) << 8) | (fromRGB.blue & 0xFF);
+        int fromGpColor = Gdip.Color_new(data.alpha << 24 | rgb);
+        if (fromGpColor is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        rgb = ((toRGB.red & 0xFF) << 16) | ((toRGB.green & 0xFF) << 8) | (toRGB.blue & 0xFF);
+        int toGpColor = Gdip.Color_new(data.alpha << 24 | rgb);
+        if (toGpColor is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        int brush = Gdip.LinearGradientBrush_new(p1, p2, fromGpColor, toGpColor);
+        Gdip.Graphics_FillRectangle(data.gdipGraphics, brush, x, y, width, height);
+        Gdip.LinearGradientBrush_delete(brush);
+        Gdip.Color_delete(fromGpColor);
+        Gdip.Color_delete(toGpColor);
+        return;
+    }
+    /* Use GradientFill if supported, only on Windows 98, 2000 and newer. */
+    /*
+    * Bug in Windows: On Windows 2000 when the device is a printer,
+    * GradientFill swaps red and blue color components, causing the
+    * gradient to be printed in the wrong color. On Windows 98 when
+    * the device is a printer, GradientFill does not fill completely
+    * to the right edge of the rectangle. The fix is not to use
+    * GradientFill for printer devices.
+    */
+    int rop2 = 0;
+    if (OS.IsWinCE) {
+        rop2 = OS.SetROP2(handle, OS.R2_COPYPEN);
+        OS.SetROP2(handle, rop2);
+    } else {
+        rop2 = OS.GetROP2(handle);
+    }
+    if (OS.IsWinNT && rop2 !is OS.R2_XORPEN && OS.GetDeviceCaps(handle, OS.TECHNOLOGY) !is OS.DT_RASPRINTER) {
+        final int hHeap = OS.GetProcessHeap();
+        final int pMesh = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, GRADIENT_RECT.sizeof + TRIVERTEX.sizeof * 2);
+        if (pMesh is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        final int pVertex = pMesh + GRADIENT_RECT.sizeof;
+
+        GRADIENT_RECT gradientRect = new GRADIENT_RECT();
+        gradientRect.UpperLeft = 0;
+        gradientRect.LowerRight = 1;
+        OS.MoveMemory(pMesh, gradientRect, GRADIENT_RECT.sizeof);
+
+        TRIVERTEX trivertex = new TRIVERTEX();
+        trivertex.x = x;
+        trivertex.y = y;
+        trivertex.Red = (short)((fromRGB.red << 8) | fromRGB.red);
+        trivertex.Green = (short)((fromRGB.green << 8) | fromRGB.green);
+        trivertex.Blue = (short)((fromRGB.blue << 8) | fromRGB.blue);
+        trivertex.Alpha = -1;
+        OS.MoveMemory(pVertex, trivertex, TRIVERTEX.sizeof);
+
+        trivertex.x = x + width;
+        trivertex.y = y + height;
+        trivertex.Red = (short)((toRGB.red << 8) | toRGB.red);
+        trivertex.Green = (short)((toRGB.green << 8) | toRGB.green);
+        trivertex.Blue = (short)((toRGB.blue << 8) | toRGB.blue);
+        trivertex.Alpha = -1;
+        OS.MoveMemory(pVertex + TRIVERTEX.sizeof, trivertex, TRIVERTEX.sizeof);
+
+        bool success = OS.GradientFill(handle, pVertex, 2, pMesh, 1, vertical ? OS.GRADIENT_FILL_RECT_V : OS.GRADIENT_FILL_RECT_H);
+        OS.HeapFree(hHeap, 0, pMesh);
+        if (success) return;
+    }
+
+    final int depth = OS.GetDeviceCaps(handle, OS.BITSPIXEL);
+    final int bitResolution = (depth >= 24) ? 8 : (depth >= 15) ? 5 : 0;
+    ImageData.fillGradientRectangle(this, data.device,
+        x, y, width, height, vertical, fromRGB, toRGB,
+        bitResolution, bitResolution, bitResolution);
+}
+
+/**
+ * Fills the interior of an oval, within the specified
+ * rectangular area, with the receiver's background
+ * color.
+ *
+ * @param x the x coordinate of the upper left corner of the oval to be filled
+ * @param y the y coordinate of the upper left corner of the oval to be filled
+ * @param width the width of the oval to be filled
+ * @param height the height of the oval to be filled
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawOval
+ */
+public void fillOval (int x, int y, int width, int height) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(FILL);
+    if (data.gdipGraphics !is 0) {
+        Gdip.Graphics_FillEllipse(data.gdipGraphics, data.gdipBrush, x, y, width, height);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) x--;
+    OS.Ellipse(handle, x, y, x + width + 1, y + height + 1);
+}
+
+/**
+ * Fills the path described by the parameter.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param path the path to fill
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see Path
+ *
+ * @since 3.1
+ */
+public void fillPath (Path path) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (path is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (path.handle is 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    initGdip();
+    checkGC(FILL);
+    int mode = OS.GetPolyFillMode(handle) is OS.WINDING ? Gdip.FillModeWinding : Gdip.FillModeAlternate;
+    Gdip.GraphicsPath_SetFillMode(path.handle, mode);
+    Gdip.Graphics_FillPath(data.gdipGraphics, data.gdipBrush, path.handle);
+}
+
+/**
+ * Fills the interior of the closed polygon which is defined by the
+ * specified array of integer coordinates, using the receiver's
+ * background color. The array contains alternating x and y values
+ * which are considered to represent points which are the vertices of
+ * the polygon. Lines are drawn between each consecutive pair, and
+ * between the first pair and last pair in the array.
+ *
+ * @param pointArray an array of alternating x and y values which are the vertices of the polygon
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT if pointArray is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawPolygon
+ */
+public void fillPolygon(int[] pointArray) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (pointArray is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    checkGC(FILL);
+    if (data.gdipGraphics !is 0) {
+        int mode = OS.GetPolyFillMode(handle) is OS.WINDING ? Gdip.FillModeWinding : Gdip.FillModeAlternate;
+        Gdip.Graphics_FillPolygon(data.gdipGraphics, data.gdipBrush, pointArray, pointArray.length / 2, mode);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        for (int i = 0; i < pointArray.length; i+=2) {
+            pointArray[i]--;
+        }
+    }
+    OS.Polygon(handle, pointArray, pointArray.length / 2);
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        for (int i = 0; i < pointArray.length; i+=2) {
+            pointArray[i]++;
+        }
+    }
+}
+
+/**
+ * Fills the interior of the rectangle specified by the arguments,
+ * using the receiver's background color.
+ *
+ * @param x the x coordinate of the rectangle to be filled
+ * @param y the y coordinate of the rectangle to be filled
+ * @param width the width of the rectangle to be filled
+ * @param height the height of the rectangle to be filled
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawRectangle(int, int, int, int)
+ */
+public void fillRectangle (int x, int y, int width, int height) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(FILL);
+    if (data.gdipGraphics !is 0) {
+        Gdip.Graphics_FillRectangle(data.gdipGraphics, data.gdipBrush, x, y, width, height);
+        return;
+    }
+    int rop2 = 0;
+    if (OS.IsWinCE) {
+        rop2 = OS.SetROP2(handle, OS.R2_COPYPEN);
+        OS.SetROP2(handle, rop2);
+    } else {
+        rop2 = OS.GetROP2(handle);
+    }
+    int dwRop = rop2 is OS.R2_XORPEN ? OS.PATINVERT : OS.PATCOPY;
+    OS.PatBlt(handle, x, y, width, height, dwRop);
+}
+
+/**
+ * Fills the interior of the specified rectangle, using the receiver's
+ * background color.
+ *
+ * @param rect the rectangle to be filled
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawRectangle(int, int, int, int)
+ */
+public void fillRectangle (Rectangle rect) {
+    if (rect is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    fillRectangle (rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * Fills the interior of the round-cornered rectangle specified by
+ * the arguments, using the receiver's background color.
+ *
+ * @param x the x coordinate of the rectangle to be filled
+ * @param y the y coordinate of the rectangle to be filled
+ * @param width the width of the rectangle to be filled
+ * @param height the height of the rectangle to be filled
+ * @param arcWidth the width of the arc
+ * @param arcHeight the height of the arc
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #drawRoundRectangle
+ */
+public void fillRoundRectangle (int x, int y, int width, int height, int arcWidth, int arcHeight) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(FILL);
+    if (data.gdipGraphics !is 0) {
+        fillRoundRectangleGdip(data.gdipGraphics, data.gdipBrush, x, y, width, height, arcWidth, arcHeight);
+        return;
+    }
+    if ((data.style & DWT.MIRRORED) !is 0) x--;
+    OS.RoundRect(handle, x,y,x+width+1,y+height+1,arcWidth, arcHeight);
+}
+
+void fillRoundRectangleGdip (int gdipGraphics, int brush, int x, int y, int width, int height, int arcWidth, int arcHeight) {
+    int nx = x;
+    int ny = y;
+    int nw = width;
+    int nh = height;
+    int naw = arcWidth;
+    int nah = arcHeight;
+
+    if (nw < 0) {
+        nw = 0 - nw;
+        nx = nx - nw;
+    }
+    if (nh < 0) {
+        nh = 0 - nh;
+        ny = ny -nh;
+    }
+    if (naw < 0)
+        naw = 0 - naw;
+    if (nah < 0)
+        nah = 0 - nah;
+
+    int path = Gdip.GraphicsPath_new(Gdip.FillModeAlternate);
+    if (path is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    if (nw > naw) {
+        if (nh > nah) {
+            Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nah, 0, -90);
+            Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nah, -90, -90);
+            Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, naw, nah, -180, -90);
+            Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny + nh - nah, naw, nah, -270, -90);
+        } else {
+            Gdip.GraphicsPath_AddArc(path, nx + nw - naw, ny, naw, nh, -270, -180);
+            Gdip.GraphicsPath_AddArc(path, nx, ny, naw, nh, -90, -180);
+        }
+    } else {
+        if (nh > nah) {
+            Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nah, 0, -180);
+            Gdip.GraphicsPath_AddArc(path, nx, ny + nh - nah, nw, nah, -180, -180);
+        } else {
+            Gdip.GraphicsPath_AddArc(path, nx, ny, nw, nh, 0, 360);
+        }
+    }
+    Gdip.GraphicsPath_CloseFigure(path);
+    Gdip.Graphics_FillPath(gdipGraphics, brush, path);
+    Gdip.GraphicsPath_delete(path);
+}
+
+void flush () {
+    if (data.gdipGraphics !is 0) {
+        Gdip.Graphics_Flush(data.gdipGraphics, 0);
+        /*
+        * Note Flush() does not flush the output to the
+        * underline HDC. This is done by calling GetHDC()
+        * followed by ReleaseHDC().
+        */
+        int hdc = Gdip.Graphics_GetHDC(data.gdipGraphics);
+        Gdip.Graphics_ReleaseHDC(data.gdipGraphics, hdc);
+    }
+}
+
+/**
+ * Returns the <em>advance width</em> of the specified character in
+ * the font which is currently selected into the receiver.
+ * <p>
+ * The advance width is defined as the horizontal distance the cursor
+ * should move after printing the character in the selected font.
+ * </p>
+ *
+ * @param ch the character to measure
+ * @return the distance in the x direction to move past the character before painting the next
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getAdvanceWidth(char ch) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(FONT);
+    if (OS.IsWinCE) {
+        SIZE size = new SIZE();
+        OS.GetTextExtentPoint32W(handle, new char[]{ch}, 1, size);
+        return size.cx;
+    }
+    int tch = ch;
+    if (ch > 0x7F) {
+        TCHAR buffer = new TCHAR(getCodePage(), ch, false);
+        tch = buffer.tcharAt(0);
+    }
+    int[] width = new int[1];
+    OS.GetCharWidth(handle, tch, tch, width);
+    return width[0];
+}
+
+/**
+ * Returns <code>true</code> if receiver is using the operating system's
+ * advanced graphics subsystem.  Otherwise, <code>false</code> is returned
+ * to indicate that normal graphics are in use.
+ * <p>
+ * Advanced graphics may not be installed for the operating system.  In this
+ * case, <code>false</code> is always returned.  Some operating system have
+ * only one graphics subsystem.  If this subsystem supports advanced graphics,
+ * then <code>true</code> is always returned.  If any graphics operation such
+ * as alpha, antialias, patterns, interpolation, paths, clipping or transformation
+ * has caused the receiver to switch from regular to advanced graphics mode,
+ * <code>true</code> is returned.  If the receiver has been explicitly switched
+ * to advanced mode and this mode is supported, <code>true</code> is returned.
+ * </p>
+ *
+ * @return the advanced value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #setAdvanced
+ *
+ * @since 3.1
+ */
+public bool getAdvanced() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.gdipGraphics !is 0;
+}
+
+/**
+ * Returns the receiver's alpha value.
+ *
+ * @return the alpha value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getAlpha() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.alpha;
+}
+
+/**
+ * Returns the receiver's anti-aliasing setting value, which will be
+ * one of <code>DWT.DEFAULT</code>, <code>DWT.OFF</code> or
+ * <code>DWT.ON</code>. Note that this controls anti-aliasing for all
+ * <em>non-text drawing</em> operations.
+ *
+ * @return the anti-aliasing setting
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #getTextAntialias
+ *
+ * @since 3.1
+ */
+public int getAntialias() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics is 0) return DWT.DEFAULT;
+    int mode = Gdip.Graphics_GetSmoothingMode(data.gdipGraphics);
+    switch (mode) {
+        case Gdip.SmoothingModeDefault: return DWT.DEFAULT;
+        case Gdip.SmoothingModeHighSpeed:
+        case Gdip.SmoothingModeNone: return DWT.OFF;
+        case Gdip.SmoothingModeAntiAlias:
+        case Gdip.SmoothingModeAntiAlias8x8:
+        case Gdip.SmoothingModeHighQuality: return DWT.ON;
+    }
+    return DWT.DEFAULT;
+}
+
+/**
+ * Returns the background color.
+ *
+ * @return the receiver's background color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Color getBackground() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return Color.win32_new(data.device, data.background);
+}
+
+/**
+ * Returns the background pattern. The default value is
+ * <code>null</code>.
+ *
+ * @return the receiver's background pattern
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Pattern
+ *
+ * @since 3.1
+ */
+public Pattern getBackgroundPattern() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.backgroundPattern;
+}
+
+/**
+ * Returns the width of the specified character in the font
+ * selected into the receiver.
+ * <p>
+ * The width is defined as the space taken up by the actual
+ * character, not including the leading and tailing whitespace
+ * or overhang.
+ * </p>
+ *
+ * @param ch the character to measure
+ * @return the width of the character
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getCharWidth(char ch) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(FONT);
+
+    /* GetCharABCWidths only succeeds on truetype fonts */
+    if (!OS.IsWinCE) {
+        int tch = ch;
+        if (ch > 0x7F) {
+            TCHAR buffer = new TCHAR(getCodePage(), ch, false);
+            tch = buffer.tcharAt (0);
+        }
+        int[] width = new int[3];
+        if (OS.GetCharABCWidths(handle, tch, tch, width)) {
+            return width[1];
+        }
+    }
+
+    /* It wasn't a truetype font */
+    TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA();
+    OS.GetTextMetrics(handle, lptm);
+    SIZE size = new SIZE();
+    OS.GetTextExtentPoint32W(handle, new char[]{ch}, 1, size);
+    return size.cx - lptm.tmOverhang;
+}
+
+/**
+ * Returns the bounding rectangle of the receiver's clipping
+ * region. If no clipping region is set, the return value
+ * will be a rectangle which covers the entire bounds of the
+ * object the receiver is drawing on.
+ *
+ * @return the bounding rectangle of the clipping region
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Rectangle getClipping() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Rect rect = new Rect();
+        Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
+        Gdip.Graphics_GetVisibleClipBounds(gdipGraphics, rect);
+        Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
+        return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
+    }
+    RECT rect = new RECT();
+    OS.GetClipBox(handle, rect);
+    return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+}
+
+/**
+ * Sets the region managed by the argument to the current
+ * clipping region of the receiver.
+ *
+ * @param region the region to fill with the clipping region
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the region is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the region is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void getClipping (Region region) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (region is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    if (region.isDisposed()) DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        int rgn = Gdip.Region_new();
+        Gdip.Graphics_GetClip(data.gdipGraphics, rgn);
+        if (Gdip.Region_IsInfinite(rgn, gdipGraphics)) {
+            Rect rect = new Rect();
+            Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
+            Gdip.Graphics_GetVisibleClipBounds(gdipGraphics, rect);
+            Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
+            OS.SetRectRgn(region.handle, rect.X, rect.Y, rect.X + rect.Width, rect.Y + rect.Height);
+        } else {
+            int matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
+            int identity = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
+            Gdip.Graphics_GetTransform(gdipGraphics, matrix);
+            Gdip.Graphics_SetTransform(gdipGraphics, identity);
+            int hRgn = Gdip.Region_GetHRGN(rgn, data.gdipGraphics);
+            Gdip.Graphics_SetTransform(gdipGraphics, matrix);
+            Gdip.Matrix_delete(identity);
+            Gdip.Matrix_delete(matrix);
+            OS.CombineRgn(region.handle, hRgn, 0, OS.RGN_COPY);
+            OS.DeleteObject(hRgn);
+        }
+        Gdip.Region_delete(rgn);
+        return;
+    }
+    POINT pt = new POINT ();
+    if (!OS.IsWinCE) OS.GetWindowOrgEx (handle, pt);
+    int result = OS.GetClipRgn (handle, region.handle);
+    if (result !is 1) {
+        RECT rect = new RECT();
+        OS.GetClipBox(handle, rect);
+        OS.SetRectRgn(region.handle, rect.left, rect.top, rect.right, rect.bottom);
+    } else {
+        OS.OffsetRgn (region.handle, pt.x, pt.y);
+    }
+    if (!OS.IsWinCE) {
+        int metaRgn = OS.CreateRectRgn (0, 0, 0, 0);
+        if (OS.GetMetaRgn (handle, metaRgn) !is 0) {
+            OS.OffsetRgn (metaRgn, pt.x, pt.y);
+            OS.CombineRgn (region.handle, metaRgn, region.handle, OS.RGN_AND);
+        }
+        OS.DeleteObject(metaRgn);
+        int hwnd = data.hwnd;
+        if (hwnd !is 0 && data.ps !is null) {
+            int sysRgn = OS.CreateRectRgn (0, 0, 0, 0);
+            if (OS.GetRandomRgn (handle, sysRgn, OS.SYSRGN) is 1) {
+                if (OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
+                    if ((OS.GetLayout(handle) & OS.LAYOUT_RTL) !is 0) {
+                        int nBytes = OS.GetRegionData (sysRgn, 0, null);
+                        int [] lpRgnData = new int [nBytes / 4];
+                        OS.GetRegionData (sysRgn, nBytes, lpRgnData);
+                        int newSysRgn = OS.ExtCreateRegion(new float [] {-1, 0, 0, 1, 0, 0}, nBytes, lpRgnData);
+                        OS.DeleteObject(sysRgn);
+                        sysRgn = newSysRgn;
+                    }
+                }
+                if (OS.IsWinNT) {
+                    OS.MapWindowPoints(0, hwnd, pt, 1);
+                    OS.OffsetRgn(sysRgn, pt.x, pt.y);
+                }
+                OS.CombineRgn (region.handle, sysRgn, region.handle, OS.RGN_AND);
+            }
+            OS.DeleteObject(sysRgn);
+        }
+    }
+}
+
+int getCodePage () {
+    if (OS.IsUnicode) return OS.CP_ACP;
+    int[] lpCs = new int[8];
+    int cs = OS.GetTextCharset(handle);
+    OS.TranslateCharsetInfo(cs, lpCs, OS.TCI_SRCCHARSET);
+    return lpCs[1];
+}
+
+int getFgBrush() {
+    return data.foregroundPattern !is null ? data.foregroundPattern.handle : data.gdipFgBrush;
+}
+
+/**
+ * Returns the receiver's fill rule, which will be one of
+ * <code>DWT.FILL_EVEN_ODD</code> or <code>DWT.FILL_WINDING</code>.
+ *
+ * @return the receiver's fill rule
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getFillRule() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (OS.IsWinCE) return DWT.FILL_EVEN_ODD;
+    return OS.GetPolyFillMode(handle) is OS.WINDING ? DWT.FILL_WINDING : DWT.FILL_EVEN_ODD;
+}
+
+/**
+ * Returns the font currently being used by the receiver
+ * to draw and measure text.
+ *
+ * @return the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Font getFont () {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return Font.win32_new(data.device, data.hFont);
+}
+
+/**
+ * Returns a FontMetrics which contains information
+ * about the font currently being used by the receiver
+ * to draw and measure text.
+ *
+ * @return font metrics for the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public FontMetrics getFontMetrics() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    checkGC(FONT);
+    TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA();
+    OS.GetTextMetrics(handle, lptm);
+    return FontMetrics.win32_new(lptm);
+}
+
+/**
+ * Returns the receiver's foreground color.
+ *
+ * @return the color used for drawing foreground things
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Color getForeground() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return Color.win32_new(data.device, data.foreground);
+}
+
+/**
+ * Returns the foreground pattern. The default value is
+ * <code>null</code>.
+ *
+ * @return the receiver's foreground pattern
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Pattern
+ *
+ * @since 3.1
+ */
+public Pattern getForegroundPattern() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.foregroundPattern;
+}
+
+/**
+ * Returns the GCData.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>GC</code>. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ *
+ * @return the receiver's GCData
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see GCData
+ *
+ * @since 3.2
+ */
+public GCData getGCData() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data;
+}
+
+/**
+ * Returns the receiver's interpolation setting, which will be one of
+ * <code>DWT.DEFAULT</code>, <code>DWT.NONE</code>,
+ * <code>DWT.LOW</code> or <code>DWT.HIGH</code>.
+ *
+ * @return the receiver's interpolation setting
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getInterpolation() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics is 0) return DWT.DEFAULT;
+    int mode = Gdip.Graphics_GetInterpolationMode(data.gdipGraphics);
+    switch (mode) {
+        case Gdip.InterpolationModeDefault: return DWT.DEFAULT;
+        case Gdip.InterpolationModeNearestNeighbor: return DWT.NONE;
+        case Gdip.InterpolationModeBilinear:
+        case Gdip.InterpolationModeLowQuality: return DWT.LOW;
+        case Gdip.InterpolationModeBicubic:
+        case Gdip.InterpolationModeHighQualityBilinear:
+        case Gdip.InterpolationModeHighQualityBicubic:
+        case Gdip.InterpolationModeHighQuality: return DWT.HIGH;
+    }
+    return DWT.DEFAULT;
+}
+
+/**
+ * Returns the receiver's line attributes.
+ *
+ * @return the line attributes used for drawing lines
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public LineAttributes getLineAttributes() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    float[] dashes = null;
+    if (data.lineDashes !is null) {
+        dashes = new float[data.lineDashes.length];
+        System.arraycopy(data.lineDashes, 0, dashes, 0, dashes.length);
+    }
+    return new LineAttributes(data.lineWidth, data.lineCap, data.lineJoin, data.lineStyle, dashes, data.lineDashesOffset, data.lineMiterLimit);
+}
+
+/**
+ * Returns the receiver's line cap style, which will be one
+ * of the constants <code>DWT.CAP_FLAT</code>, <code>DWT.CAP_ROUND</code>,
+ * or <code>DWT.CAP_SQUARE</code>.
+ *
+ * @return the cap style used for drawing lines
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getLineCap() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.lineCap;
+}
+
+/**
+ * Returns the receiver's line dash style. The default value is
+ * <code>null</code>.
+ *
+ * @return the line dash style used for drawing lines
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int[] getLineDash() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.lineDashes is null) return null;
+    int[] lineDashes = new int[data.lineDashes.length];
+    for (int i = 0; i < lineDashes.length; i++) {
+        lineDashes[i] = (int)data.lineDashes[i];
+    }
+    return lineDashes;
+}
+
+/**
+ * Returns the receiver's line join style, which will be one
+ * of the constants <code>DWT.JOIN_MITER</code>, <code>DWT.JOIN_ROUND</code>,
+ * or <code>DWT.JOIN_BEVEL</code>.
+ *
+ * @return the join style used for drawing lines
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getLineJoin() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.lineJoin;
+}
+
+/**
+ * Returns the receiver's line style, which will be one
+ * of the constants <code>DWT.LINE_SOLID</code>, <code>DWT.LINE_DASH</code>,
+ * <code>DWT.LINE_DOT</code>, <code>DWT.LINE_DASHDOT</code> or
+ * <code>DWT.LINE_DASHDOTDOT</code>.
+ *
+ * @return the style used for drawing lines
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getLineStyle() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.lineStyle;
+}
+
+/**
+ * Returns the width that will be used when drawing lines
+ * for all of the figure drawing operations (that is,
+ * <code>drawLine</code>, <code>drawRectangle</code>,
+ * <code>drawPolyline</code>, and so forth.
+ *
+ * @return the receiver's line width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getLineWidth() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return (int)data.lineWidth;
+}
+
+/**
+ * Returns the receiver's style information.
+ * <p>
+ * Note that the value which is returned by this method <em>may
+ * not match</em> the value which was provided to the constructor
+ * when the receiver was created. This can occur when the underlying
+ * operating system does not support a particular combination of
+ * requested styles.
+ * </p>
+ *
+ * @return the style bits
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public int getStyle () {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return data.style;
+}
+
+/**
+ * Returns the receiver's text drawing anti-aliasing setting value,
+ * which will be one of <code>DWT.DEFAULT</code>, <code>DWT.OFF</code> or
+ * <code>DWT.ON</code>. Note that this controls anti-aliasing
+ * <em>only</em> for text drawing operations.
+ *
+ * @return the anti-aliasing setting
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #getAntialias
+ *
+ * @since 3.1
+ */
+public int getTextAntialias() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics is 0) return DWT.DEFAULT;
+    int mode = Gdip.Graphics_GetTextRenderingHint(data.gdipGraphics);
+    switch (mode) {
+        case Gdip.TextRenderingHintSystemDefault: return DWT.DEFAULT;
+        case Gdip.TextRenderingHintSingleBitPerPixel:
+        case Gdip.TextRenderingHintSingleBitPerPixelGridFit: return DWT.OFF;
+        case Gdip.TextRenderingHintAntiAlias:
+        case Gdip.TextRenderingHintAntiAliasGridFit:
+        case Gdip.TextRenderingHintClearTypeGridFit: return DWT.ON;
+    }
+    return DWT.DEFAULT;
+}
+
+/**
+ * Sets the parameter to the transform that is currently being
+ * used by the receiver.
+ *
+ * @param transform the destination to copy the transform into
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Transform
+ *
+ * @since 3.1
+ */
+public void getTransform(Transform transform) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (transform is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (transform.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        Gdip.Graphics_GetTransform(gdipGraphics, transform.handle);
+        int identity = identity();
+        Gdip.Matrix_Invert(identity);
+        Gdip.Matrix_Multiply(transform.handle, identity, Gdip.MatrixOrderAppend);
+        Gdip.Matrix_delete(identity);
+    } else {
+        transform.setElements(1, 0, 0, 1, 0, 0);
+    }
+}
+
+/**
+ * Returns <code>true</code> if this GC is drawing in the mode
+ * where the resulting color in the destination is the
+ * <em>exclusive or</em> of the color values in the source
+ * and the destination, and <code>false</code> if it is
+ * drawing in the mode where the destination color is being
+ * replaced with the source color value.
+ *
+ * @return <code>true</code> true if the receiver is in XOR mode, and false otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public bool getXORMode() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    int rop2 = 0;
+    if (OS.IsWinCE) {
+        rop2 = OS.SetROP2 (handle, OS.R2_COPYPEN);
+        OS.SetROP2 (handle, rop2);
+    } else {
+        rop2 = OS.GetROP2(handle);
+    }
+    return rop2 is OS.R2_XORPEN;
+}
+
+void initGdip() {
+    data.device.checkGDIP();
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) return;
+    /*
+    * Feature in GDI+. The GDI+ clipping set with Graphics->SetClip()
+    * is always intersected with the GDI clipping at the time the
+    * GDI+ graphics is created.  This means that the clipping
+    * cannot be reset.  The fix is to clear the clipping before
+    * the GDI+ graphics is created and reset it afterwards.
+    */
+    int hRgn = OS.CreateRectRgn(0, 0, 0, 0);
+    int result = OS.GetClipRgn(handle, hRgn);
+    if (!OS.IsWinCE) {
+        POINT pt = new POINT ();
+        OS.GetWindowOrgEx (handle, pt);
+        OS.OffsetRgn (hRgn, pt.x, pt.y);
+    }
+    OS.SelectClipRgn(handle, 0);
+
+    /*
+    * Bug in GDI+.  GDI+ does not work when the HDC layout is RTL.  There
+    * are many issues like pixel corruption, but the most visible problem
+    * is that it does not have an effect when drawing to an bitmap.  The
+    * fix is to clear the bit before creating the GDI+ graphics and install
+    * a mirroring matrix ourselves.
+    */
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        OS.SetLayout(handle, OS.GetLayout(handle) & ~OS.LAYOUT_RTL);
+    }
+
+    gdipGraphics = data.gdipGraphics = Gdip.Graphics_new(handle);
+    if (gdipGraphics is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    Gdip.Graphics_SetPageUnit(gdipGraphics, Gdip.UnitPixel);
+    Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        int matrix = identity();
+        Gdip.Graphics_SetTransform(gdipGraphics, matrix);
+        Gdip.Matrix_delete(matrix);
+    }
+    if (result is 1) setClipping(hRgn);
+    OS.DeleteObject(hRgn);
+    data.state = 0;
+    if (data.hPen !is 0) {
+        OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN));
+        OS.DeleteObject(data.hPen);
+        data.hPen = 0;
+    }
+    if (data.hBrush !is 0) {
+        OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH));
+        OS.DeleteObject(data.hBrush);
+        data.hBrush = 0;
+    }
+}
+
+int identity() {
+    if ((data.style & DWT.MIRRORED) !is 0) {
+        int width = 0;
+        Image image = data.image;
+        if (image !is null) {
+            BITMAP bm = new BITMAP();
+            OS.GetObject(image.handle, BITMAP.sizeof, bm);
+            width = bm.bmWidth;
+        } else if (data.hwnd !is 0) {
+            RECT rect = new RECT();
+            OS.GetClientRect(data.hwnd, rect);
+            width = rect.right - rect.left;
+        }
+        POINT pt = new POINT ();
+        if (!OS.IsWinCE) OS.GetWindowOrgEx (handle, pt);
+        return Gdip.Matrix_new(-1, 0, 0, 1, width + 2 * pt.x, 0);
+    }
+    return Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
+}
+
+void init(Drawable drawable, GCData data, int hDC) {
+    int foreground = data.foreground;
+    if (foreground !is -1) {
+        data.state &= ~(FOREGROUND | FOREGROUND_TEXT | PEN);
+    } else {
+        data.foreground = OS.GetTextColor(hDC);
+    }
+    int background = data.background;
+    if (background !is -1) {
+        data.state &= ~(BACKGROUND | BACKGROUND_TEXT | BRUSH);
+    } else {
+        data.background = OS.GetBkColor(hDC);
+    }
+    data.state &= ~(NULL_BRUSH | NULL_PEN);
+    int hFont = data.hFont;
+    if (hFont !is 0) {
+        data.state &= ~FONT;
+    } else {
+        hFont = OS.GetCurrentObject(hDC, OS.OBJ_FONT);
+    }
+    int hPalette = data.device.hPalette;
+    if (hPalette !is 0) {
+        OS.SelectPalette(hDC, hPalette, true);
+        OS.RealizePalette(hDC);
+    }
+    Image image = data.image;
+    if (image !is null) {
+        data.hNullBitmap = OS.SelectObject(hDC, image.handle);
+        image.memGC = this;
+    }
+    int layout = data.layout;
+    if (layout !is -1) {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
+            int flags = OS.GetLayout(hDC);
+            if ((flags & OS.LAYOUT_RTL) !is (layout & OS.LAYOUT_RTL)) {
+                flags &= ~OS.LAYOUT_RTL;
+                OS.SetLayout(hDC, flags | layout);
+            }
+            if ((data.style & DWT.RIGHT_TO_LEFT) !is 0) data.style |= DWT.MIRRORED;
+        }
+    }
+    this.drawable = drawable;
+    this.data = data;
+    handle = hDC;
+}
+
+/**
+ * Returns an integer hash code for the receiver. Any two
+ * objects that return <code>true</code> when passed to
+ * <code>equals</code> must return the same value for this
+ * method.
+ *
+ * @return the receiver's hash
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #equals
+ */
+public int hashCode () {
+    return handle;
+}
+
+/**
+ * Returns <code>true</code> if the receiver has a clipping
+ * region set into it, and <code>false</code> otherwise.
+ * If this method returns false, the receiver will draw on all
+ * available space in the destination. If it returns true,
+ * it will draw only in the area that is covered by the region
+ * that can be accessed with <code>getClipping(region)</code>.
+ *
+ * @return <code>true</code> if the GC has a clipping region, and <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public bool isClipped() {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    int region = OS.CreateRectRgn(0, 0, 0, 0);
+    int result = OS.GetClipRgn(handle, region);
+    OS.DeleteObject(region);
+    return result > 0;
+}
+
+/**
+ * Returns <code>true</code> if the GC has been disposed,
+ * and <code>false</code> otherwise.
+ * <p>
+ * This method gets the dispose state for the GC.
+ * When a GC has been disposed, it is an error to
+ * invoke any other method using the GC.
+ *
+ * @return <code>true</code> when the GC is disposed and <code>false</code> otherwise
+ */
+public bool isDisposed() {
+    return handle is 0;
+}
+
+float measureSpace(int font, int format) {
+    PointF pt = new PointF();
+    RectF bounds = new RectF();
+    Gdip.Graphics_MeasureString(data.gdipGraphics, new char[]{' '}, 1, font, pt, format, bounds);
+    return bounds.Width;
+}
+
+/**
+ * Sets the receiver to always use the operating system's advanced graphics
+ * subsystem for all graphics operations if the argument is <code>true</code>.
+ * If the argument is <code>false</code>, the advanced graphics subsystem is
+ * no longer used, advanced graphics state is cleared and the normal graphics
+ * subsystem is used from now on.
+ * <p>
+ * Normally, the advanced graphics subsystem is invoked automatically when
+ * any one of the alpha, antialias, patterns, interpolation, paths, clipping
+ * or transformation operations in the receiver is requested.  When the receiver
+ * is switched into advanced mode, the advanced graphics subsystem performs both
+ * advanced and normal graphics operations.  Because the two subsystems are
+ * different, their output may differ.  Switching to advanced graphics before
+ * any graphics operations are performed ensures that the output is consistent.
+ * </p><p>
+ * Advanced graphics may not be installed for the operating system.  In this
+ * case, this operation does nothing.  Some operating system have only one
+ * graphics subsystem, so switching from normal to advanced graphics does
+ * nothing.  However, switching from advanced to normal graphics will always
+ * clear the advanced graphics state, even for operating systems that have
+ * only one graphics subsystem.
+ * </p>
+ *
+ * @param advanced the new advanced graphics state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #setAlpha
+ * @see #setAntialias
+ * @see #setBackgroundPattern
+ * @see #setClipping(Path)
+ * @see #setForegroundPattern
+ * @see #setLineAttributes
+ * @see #setInterpolation
+ * @see #setTextAntialias
+ * @see #setTransform
+ * @see #getAdvanced
+ *
+ * @since 3.1
+ */
+public void setAdvanced(bool advanced) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (advanced && data.gdipGraphics !is 0) return;
+    if (advanced) {
+        try {
+            initGdip();
+        } catch (DWTException e) {}
+    } else {
+        disposeGdip();
+        data.alpha = 0xFF;
+        data.backgroundPattern = data.foregroundPattern = null;
+        data.state = 0;
+        setClipping(0);
+        if ((data.style & DWT.MIRRORED) !is 0) {
+            OS.SetLayout(handle, OS.GetLayout(handle) | OS.LAYOUT_RTL);
+        }
+    }
+}
+
+/**
+ * Sets the receiver's anti-aliasing value to the parameter,
+ * which must be one of <code>DWT.DEFAULT</code>, <code>DWT.OFF</code>
+ * or <code>DWT.ON</code>. Note that this controls anti-aliasing for all
+ * <em>non-text drawing</em> operations.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param antialias the anti-aliasing setting
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter is not one of <code>DWT.DEFAULT</code>,
+ *                                 <code>DWT.OFF</code> or <code>DWT.ON</code></li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see #getAdvanced
+ * @see #setAdvanced
+ * @see #setTextAntialias
+ *
+ * @since 3.1
+ */
+public void setAntialias(int antialias) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics is 0 && antialias is DWT.DEFAULT) return;
+    int mode = 0;
+    switch (antialias) {
+        case DWT.DEFAULT:
+            mode = Gdip.SmoothingModeDefault;
+            break;
+        case DWT.OFF:
+            mode = Gdip.SmoothingModeNone;
+            break;
+        case DWT.ON:
+            mode = Gdip.SmoothingModeAntiAlias;
+            break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    initGdip();
+    Gdip.Graphics_SetSmoothingMode(data.gdipGraphics, mode);
+}
+
+/**
+ * Sets the receiver's alpha value.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ * @param alpha the alpha value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see #getAdvanced
+ * @see #setAdvanced
+ *
+ * @since 3.1
+ */
+public void setAlpha(int alpha) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics is 0 && (alpha & 0xFF) is 0xFF) return;
+    initGdip();
+    data.alpha = alpha & 0xFF;
+    data.state &= ~(BACKGROUND | FOREGROUND);
+}
+
+/**
+ * Sets the background color. The background color is used
+ * for fill operations and as the background color when text
+ * is drawn.
+ *
+ * @param color the new background color for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the color is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setBackground (Color color) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (color is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (data.backgroundPattern is null && data.background is color.handle) return;
+    data.backgroundPattern = null;
+    data.background = color.handle;
+    data.state &= ~(BACKGROUND | BACKGROUND_TEXT);
+}
+
+/**
+ * Sets the background pattern. The default value is <code>null</code>.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param pattern the new background pattern
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see Pattern
+ * @see #getAdvanced
+ * @see #setAdvanced
+ *
+ * @since 3.1
+ */
+public void setBackgroundPattern (Pattern pattern) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (pattern !is null && pattern.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (data.gdipGraphics is 0 && pattern is null) return;
+    initGdip();
+    if (data.backgroundPattern is pattern) return;
+    data.backgroundPattern = pattern;
+    data.state &= ~BACKGROUND;
+}
+
+void setClipping(int clipRgn) {
+    int hRgn = clipRgn;
+    int gdipGraphics = data.gdipGraphics;
+    if (gdipGraphics !is 0) {
+        if (hRgn !is 0) {
+            int region = Gdip.Region_new(hRgn);
+            Gdip.Graphics_SetClip(gdipGraphics, region, Gdip.CombineModeReplace);
+            Gdip.Region_delete(region);
+        } else {
+            Gdip.Graphics_ResetClip(gdipGraphics);
+        }
+    } else {
+        POINT pt = null;
+        if (hRgn !is 0 && !OS.IsWinCE) {
+            pt = new POINT();
+            OS.GetWindowOrgEx(handle, pt);
+            OS.OffsetRgn(hRgn, -pt.x, -pt.y);
+        }
+        OS.SelectClipRgn(handle, hRgn);
+        if (hRgn !is 0 && !OS.IsWinCE) {
+            OS.OffsetRgn(hRgn, pt.x, pt.y);
+        }
+    }
+    if (hRgn !is 0 && hRgn !is clipRgn) {
+        OS.DeleteObject(hRgn);
+    }
+}
+
+/**
+ * Sets the area of the receiver which can be changed
+ * by drawing operations to the rectangular area specified
+ * by the arguments.
+ *
+ * @param x the x coordinate of the clipping rectangle
+ * @param y the y coordinate of the clipping rectangle
+ * @param width the width of the clipping rectangle
+ * @param height the height of the clipping rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setClipping (int x, int y, int width, int height) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    int hRgn = OS.CreateRectRgn(x, y, x + width, y + height);
+    setClipping(hRgn);
+    OS.DeleteObject(hRgn);
+}
+
+/**
+ * Sets the area of the receiver which can be changed
+ * by drawing operations to the path specified
+ * by the argument.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param path the clipping path.
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the path has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see Path
+ * @see #getAdvanced
+ * @see #setAdvanced
+ *
+ * @since 3.1
+ */
+public void setClipping (Path path) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (path !is null && path.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    setClipping(0);
+    if (path !is null) {
+        initGdip();
+        int mode = OS.GetPolyFillMode(handle) is OS.WINDING ? Gdip.FillModeWinding : Gdip.FillModeAlternate;
+        Gdip.GraphicsPath_SetFillMode(path.handle, mode);
+        Gdip.Graphics_SetClip(data.gdipGraphics, path.handle);
+    }
+}
+
+/**
+ * Sets the area of the receiver which can be changed
+ * by drawing operations to the rectangular area specified
+ * by the argument.  Specifying <code>null</code> for the
+ * rectangle reverts the receiver's clipping area to its
+ * original value.
+ *
+ * @param rect the clipping rectangle or <code>null</code>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setClipping (Rectangle rect) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (rect is null) {
+        setClipping(0);
+    } else {
+        setClipping(rect.x, rect.y, rect.width, rect.height);
+    }
+}
+
+/**
+ * Sets the area of the receiver which can be changed
+ * by drawing operations to the region specified
+ * by the argument.  Specifying <code>null</code> for the
+ * region reverts the receiver's clipping area to its
+ * original value.
+ *
+ * @param region the clipping region or <code>null</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the region has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setClipping (Region region) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (region !is null && region.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    setClipping(region !is null ? region.handle : 0);
+}
+
+/**
+ * Sets the receiver's fill rule to the parameter, which must be one of
+ * <code>DWT.FILL_EVEN_ODD</code> or <code>DWT.FILL_WINDING</code>.
+ *
+ * @param rule the new fill rule
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the rule is not one of <code>DWT.FILL_EVEN_ODD</code>
+ *                                 or <code>DWT.FILL_WINDING</code></li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setFillRule(int rule) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (OS.IsWinCE) return;
+    int mode = OS.ALTERNATE;
+    switch (rule) {
+        case DWT.FILL_WINDING: mode = OS.WINDING; break;
+        case DWT.FILL_EVEN_ODD: mode = OS.ALTERNATE; break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    OS.SetPolyFillMode(handle, mode);
+}
+
+/**
+ * Sets the font which will be used by the receiver
+ * to draw and measure text to the argument. If the
+ * argument is null, then a default font appropriate
+ * for the platform will be used instead.
+ *
+ * @param font the new font for the receiver, or null to indicate a default font
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setFont (Font font) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (font !is null && font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    data.hFont = font !is null ? font.handle : data.device.systemFont;
+    data.state &= ~FONT;
+}
+
+/**
+ * Sets the foreground color. The foreground color is used
+ * for drawing operations including when text is drawn.
+ *
+ * @param color the new foreground color for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the color is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setForeground (Color color) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (color is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (data.foregroundPattern is null && color.handle is data.foreground) return;
+    data.foregroundPattern = null;
+    data.foreground = color.handle;
+    data.state &= ~(FOREGROUND | FOREGROUND_TEXT);
+}
+
+/**
+ * Sets the foreground pattern. The default value is <code>null</code>.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ * @param pattern the new foreground pattern
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see Pattern
+ * @see #getAdvanced
+ * @see #setAdvanced
+ *
+ * @since 3.1
+ */
+public void setForegroundPattern (Pattern pattern) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (pattern !is null && pattern.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (data.gdipGraphics is 0 && pattern is null) return;
+    initGdip();
+    if (data.foregroundPattern is pattern) return;
+    data.foregroundPattern = pattern;
+    data.state &= ~FOREGROUND;
+}
+
+/**
+ * Sets the receiver's interpolation setting to the parameter, which
+ * must be one of <code>DWT.DEFAULT</code>, <code>DWT.NONE</code>,
+ * <code>DWT.LOW</code> or <code>DWT.HIGH</code>.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param interpolation the new interpolation setting
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the rule is not one of <code>DWT.DEFAULT</code>,
+ *                                 <code>DWT.NONE</code>, <code>DWT.LOW</code> or <code>DWT.HIGH</code>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see #getAdvanced
+ * @see #setAdvanced
+ *
+ * @since 3.1
+ */
+public void setInterpolation(int interpolation) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics is 0 && interpolation is DWT.DEFAULT) return;
+    int mode = 0;
+    switch (interpolation) {
+        case DWT.DEFAULT: mode = Gdip.InterpolationModeDefault; break;
+        case DWT.NONE: mode = Gdip.InterpolationModeNearestNeighbor; break;
+        case DWT.LOW: mode = Gdip.InterpolationModeLowQuality; break;
+        case DWT.HIGH: mode = Gdip.InterpolationModeHighQuality; break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    initGdip();
+    Gdip.Graphics_SetInterpolationMode(data.gdipGraphics, mode);
+}
+
+/**
+ * Sets the receiver's line attributes.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ * @param attributes the line attributes
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the attributes is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if any of the line attributes is not valid</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see LineAttributes
+ * @see #getAdvanced
+ * @see #setAdvanced
+ *
+ * @since 3.3
+ */
+public void setLineAttributes(LineAttributes attributes) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (attributes is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    int mask = 0;
+    float lineWidth = attributes.width;
+    if (lineWidth !is data.lineWidth) {
+        mask |= LINE_WIDTH | DRAW_OFFSET;
+    }
+    int lineStyle = attributes.style;
+    if (lineStyle !is data.lineStyle) {
+        mask |= LINE_STYLE;
+        switch (lineStyle) {
+            case DWT.LINE_SOLID:
+            case DWT.LINE_DASH:
+            case DWT.LINE_DOT:
+            case DWT.LINE_DASHDOT:
+            case DWT.LINE_DASHDOTDOT:
+                break;
+            case DWT.LINE_CUSTOM:
+                if (attributes.dash is null) lineStyle = DWT.LINE_SOLID;
+                break;
+            default:
+                DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        }
+    }
+    int join = attributes.join;
+    if (join !is data.lineJoin) {
+        mask |= LINE_JOIN;
+        switch (join) {
+            case DWT.CAP_ROUND:
+            case DWT.CAP_FLAT:
+            case DWT.CAP_SQUARE:
+                break;
+            default:
+                DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        }
+    }
+    int cap = attributes.join;
+    if (cap !is data.lineCap) {
+        mask |= LINE_CAP;
+        switch (cap) {
+            case DWT.JOIN_MITER:
+            case DWT.JOIN_ROUND:
+            case DWT.JOIN_BEVEL:
+                break;
+            default:
+                DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        }
+    }
+    float[] dashes = attributes.dash;
+    float[] lineDashes = data.lineDashes;
+    if (dashes !is null && dashes.length > 0) {
+        bool changed = lineDashes is null || lineDashes.length !is dashes.length;
+        for (int i = 0; i < dashes.length; i++) {
+            float dash = dashes[i];
+            if (dash <= 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+            if (!changed && lineDashes[i] !is dash) changed = true;
+        }
+        if (changed) {
+            float[] newDashes = new float[dashes.length];
+            System.arraycopy(dashes, 0, newDashes, 0, dashes.length);
+            dashes = newDashes;
+            mask |= LINE_STYLE;
+        } else {
+            dashes = lineDashes;
+        }
+    } else {
+        if (lineDashes !is null && lineDashes.length > 0) {
+            mask |= LINE_STYLE;
+        } else {
+            dashes = lineDashes;
+        }
+    }
+    float dashOffset = attributes.dashOffset;
+    if (dashOffset !is data.lineDashesOffset) {
+        mask |= LINE_STYLE;
+    }
+    float miterLimit = attributes.miterLimit;
+    if (miterLimit !is data.lineMiterLimit) {
+        mask |= LINE_MITERLIMIT;
+    }
+    initGdip();
+    if (mask is 0) return;
+    data.lineWidth = lineWidth;
+    data.lineStyle = lineStyle;
+    data.lineCap = cap;
+    data.lineJoin = join;
+    data.lineDashes = dashes;
+    data.lineDashesOffset = dashOffset;
+    data.lineMiterLimit = miterLimit;
+    data.state &= ~mask;
+}
+
+/**
+ * Sets the receiver's line cap style to the argument, which must be one
+ * of the constants <code>DWT.CAP_FLAT</code>, <code>DWT.CAP_ROUND</code>,
+ * or <code>DWT.CAP_SQUARE</code>.
+ *
+ * @param cap the cap style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setLineCap(int cap) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.lineCap is cap) return;
+    switch (cap) {
+        case DWT.CAP_ROUND:
+        case DWT.CAP_FLAT:
+        case DWT.CAP_SQUARE:
+            break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    data.lineCap = cap;
+    data.state &= ~LINE_CAP;
+}
+
+/**
+ * Sets the receiver's line dash style to the argument. The default
+ * value is <code>null</code>. If the argument is not <code>null</code>,
+ * the receiver's line style is set to <code>DWT.LINE_CUSTOM</code>, otherwise
+ * it is set to <code>DWT.LINE_SOLID</code>.
+ *
+ * @param dashes the dash style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if any of the values in the array is less than or equal 0</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setLineDash(int[] dashes) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    float[] lineDashes = data.lineDashes;
+    if (dashes !is null && dashes.length > 0) {
+        bool changed = data.lineStyle !is DWT.LINE_CUSTOM || lineDashes is null || lineDashes.length !is dashes.length;
+        for (int i = 0; i < dashes.length; i++) {
+            int dash = dashes[i];
+            if (dash <= 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+            if (!changed && lineDashes[i] !is dash) changed = true;
+        }
+        if (!changed) return;
+        data.lineDashes = new float[dashes.length];
+        for (int i = 0; i < dashes.length; i++) {
+            data.lineDashes[i] = dashes[i];
+        }
+        data.lineStyle = DWT.LINE_CUSTOM;
+    } else {
+        if (data.lineStyle is DWT.LINE_SOLID && (lineDashes is null || lineDashes.length is 0)) return;
+        data.lineDashes = null;
+        data.lineStyle = DWT.LINE_SOLID;
+    }
+    data.state &= ~LINE_STYLE;
+}
+
+/**
+ * Sets the receiver's line join style to the argument, which must be one
+ * of the constants <code>DWT.JOIN_MITER</code>, <code>DWT.JOIN_ROUND</code>,
+ * or <code>DWT.JOIN_BEVEL</code>.
+ *
+ * @param join the join style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setLineJoin(int join) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.lineJoin is join) return;
+    switch (join) {
+        case DWT.JOIN_MITER:
+        case DWT.JOIN_ROUND:
+        case DWT.JOIN_BEVEL:
+            break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    data.lineJoin = join;
+    data.state &= ~LINE_JOIN;
+}
+
+/**
+ * Sets the receiver's line style to the argument, which must be one
+ * of the constants <code>DWT.LINE_SOLID</code>, <code>DWT.LINE_DASH</code>,
+ * <code>DWT.LINE_DOT</code>, <code>DWT.LINE_DASHDOT</code> or
+ * <code>DWT.LINE_DASHDOTDOT</code>.
+ *
+ * @param lineStyle the style to be used for drawing lines
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the style is not valid</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setLineStyle(int lineStyle) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.lineStyle is lineStyle) return;
+    switch (lineStyle) {
+        case DWT.LINE_SOLID:
+        case DWT.LINE_DASH:
+        case DWT.LINE_DOT:
+        case DWT.LINE_DASHDOT:
+        case DWT.LINE_DASHDOTDOT:
+            break;
+        case DWT.LINE_CUSTOM:
+            if (data.lineDashes is null) lineStyle = DWT.LINE_SOLID;
+            break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    data.lineStyle = lineStyle;
+    data.state &= ~LINE_STYLE;
+}
+
+/**
+ * Sets the width that will be used when drawing lines
+ * for all of the figure drawing operations (that is,
+ * <code>drawLine</code>, <code>drawRectangle</code>,
+ * <code>drawPolyline</code>, and so forth.
+ * <p>
+ * Note that line width of zero is used as a hint to
+ * indicate that the fastest possible line drawing
+ * algorithms should be used. This means that the
+ * output may be different from line width one.
+ * </p>
+ *
+ * @param lineWidth the width of a line
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setLineWidth(int lineWidth) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.lineWidth is lineWidth) return;
+    data.lineWidth = lineWidth;
+    data.state &= ~(LINE_WIDTH | DRAW_OFFSET);
+}
+
+/**
+ * If the argument is <code>true</code>, puts the receiver
+ * in a drawing mode where the resulting color in the destination
+ * is the <em>exclusive or</em> of the color values in the source
+ * and the destination, and if the argument is <code>false</code>,
+ * puts the receiver in a drawing mode where the destination color
+ * is replaced with the source color value.
+ * <p>
+ * Note that this mode in fundamentally unsupportable on certain
+ * platforms, notably Carbon (Mac OS X). Clients that want their
+ * code to run on all platforms need to avoid this method.
+ * </p>
+ *
+ * @param xor if <code>true</code>, then <em>xor</em> mode is used, otherwise <em>source copy</em> mode is used
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @deprecated this functionality is not supported on some platforms
+ */
+public void setXORMode(bool xor) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    OS.SetROP2(handle, xor ? OS.R2_XORPEN : OS.R2_COPYPEN);
+}
+
+/**
+ * Sets the receiver's text anti-aliasing value to the parameter,
+ * which must be one of <code>DWT.DEFAULT</code>, <code>DWT.OFF</code>
+ * or <code>DWT.ON</code>. Note that this controls anti-aliasing only
+ * for all <em>text drawing</em> operations.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param antialias the anti-aliasing setting
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter is not one of <code>DWT.DEFAULT</code>,
+ *                                 <code>DWT.OFF</code> or <code>DWT.ON</code></li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see #getAdvanced
+ * @see #setAdvanced
+ * @see #setAntialias
+ *
+ * @since 3.1
+ */
+public void setTextAntialias(int antialias) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (data.gdipGraphics is 0 && antialias is DWT.DEFAULT) return;
+    int textMode = 0;
+    switch (antialias) {
+        case DWT.DEFAULT:
+            textMode = Gdip.TextRenderingHintSystemDefault;
+            break;
+        case DWT.OFF:
+            textMode = Gdip.TextRenderingHintSingleBitPerPixelGridFit;
+            break;
+        case DWT.ON:
+            int[] type = new int[1];
+            OS.SystemParametersInfo(OS.SPI_GETFONTSMOOTHINGTYPE, 0, type, 0);
+            if (type[0] is OS.FE_FONTSMOOTHINGCLEARTYPE) {
+                textMode = Gdip.TextRenderingHintClearTypeGridFit;
+            } else {
+                textMode = Gdip.TextRenderingHintAntiAliasGridFit;
+            }
+            break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    initGdip();
+    Gdip.Graphics_SetTextRenderingHint(data.gdipGraphics, textMode);
+}
+
+/**
+ * Sets the transform that is currently being used by the receiver. If
+ * the argument is <code>null</code>, the current transform is set to
+ * the identity transform.
+ * <p>
+ * This operation requires the operating system's advanced
+ * graphics subsystem which may not be available on some
+ * platforms.
+ * </p>
+ *
+ * @param transform the transform to set
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
+ * </ul>
+ *
+ * @see Transform
+ * @see #getAdvanced
+ * @see #setAdvanced
+ *
+ * @since 3.1
+ */
+public void setTransform(Transform transform) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (transform !is null && transform.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (data.gdipGraphics is 0 && transform is null) return;
+    initGdip();
+    int identity = identity();
+    if (transform !is null) {
+         Gdip.Matrix_Multiply(identity, transform.handle, Gdip.MatrixOrderPrepend);
+    }
+    Gdip.Graphics_SetTransform(data.gdipGraphics, identity);
+    Gdip.Matrix_delete(identity);
+    data.state &= ~DRAW_OFFSET;
+}
+
+/**
+ * Returns the extent of the given string. No tab
+ * expansion or carriage return processing will be performed.
+ * <p>
+ * The <em>extent</em> of a string is the width and height of
+ * the rectangular area it would cover if drawn in a particular
+ * font (in this case, the current font in the receiver).
+ * </p>
+ *
+ * @param string the string to measure
+ * @return a point containing the extent of the string
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Point stringExtent(String string) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (string is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    checkGC(FONT);
+    int length = string.length();
+    if (data.gdipGraphics !is 0) {
+        PointF pt = new PointF();
+        RectF bounds = new RectF();
+        char[] buffer;
+        if (length !is 0) {
+            buffer = new char [length];
+            string.getChars(0, length, buffer, 0);
+        } else {
+            buffer = new char[]{' '};
+        }
+        int format = Gdip.StringFormat_Clone(Gdip.StringFormat_GenericTypographic());
+        int formatFlags = Gdip.StringFormat_GetFormatFlags(format) | Gdip.StringFormatFlagsMeasureTrailingSpaces;
+        if ((data.style & DWT.MIRRORED) !is 0) formatFlags |= Gdip.StringFormatFlagsDirectionRightToLeft;
+        Gdip.StringFormat_SetFormatFlags(format, formatFlags);
+        Gdip.Graphics_MeasureString(data.gdipGraphics, buffer, buffer.length, data.gdipFont, pt, format, bounds);
+        Gdip.StringFormat_delete(format);
+        return new Point(length is 0 ? 0 : Math.round(bounds.Width), Math.round(bounds.Height));
+    }
+    SIZE size = new SIZE();
+    if (length is 0) {
+//      OS.GetTextExtentPoint32(handle, SPACE, SPACE.length(), size);
+        OS.GetTextExtentPoint32W(handle, new char[]{' '}, 1, size);
+        return new Point(0, size.cy);
+    } else {
+//      TCHAR buffer = new TCHAR (getCodePage(), string, false);
+        char[] buffer = new char [length];
+        string.getChars(0, length, buffer, 0);
+        OS.GetTextExtentPoint32W(handle, buffer, length, size);
+        return new Point(size.cx, size.cy);
+    }
+}
+
+/**
+ * Returns the extent of the given string. Tab expansion and
+ * carriage return processing are performed.
+ * <p>
+ * The <em>extent</em> of a string is the width and height of
+ * the rectangular area it would cover if drawn in a particular
+ * font (in this case, the current font in the receiver).
+ * </p>
+ *
+ * @param string the string to measure
+ * @return a point containing the extent of the string
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Point textExtent(String string) {
+    return textExtent(string, DWT.DRAW_DELIMITER | DWT.DRAW_TAB);
+}
+
+/**
+ * Returns the extent of the given string. Tab expansion, line
+ * delimiter and mnemonic processing are performed according to
+ * the specified flags, which can be a combination of:
+ * <dl>
+ * <dt><b>DRAW_DELIMITER</b></dt>
+ * <dd>draw multiple lines</dd>
+ * <dt><b>DRAW_TAB</b></dt>
+ * <dd>expand tabs</dd>
+ * <dt><b>DRAW_MNEMONIC</b></dt>
+ * <dd>underline the mnemonic character</dd>
+ * <dt><b>DRAW_TRANSPARENT</b></dt>
+ * <dd>transparent background</dd>
+ * </dl>
+ * <p>
+ * The <em>extent</em> of a string is the width and height of
+ * the rectangular area it would cover if drawn in a particular
+ * font (in this case, the current font in the receiver).
+ * </p>
+ *
+ * @param string the string to measure
+ * @param flags the flags specifying how to process the text
+ * @return a point containing the extent of the string
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Point textExtent(String string, int flags) {
+    if (handle is 0) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (string is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    checkGC(FONT);
+    if (data.gdipGraphics !is 0) {
+        PointF pt = new PointF();
+        RectF bounds = new RectF();
+        char[] buffer;
+        int length = string.length();
+        if (length !is 0) {
+            buffer = new char [length];
+            string.getChars(0, length, buffer, 0);
+        } else {
+            buffer = new char[]{' '};
+        }
+        int format = Gdip.StringFormat_Clone(Gdip.StringFormat_GenericTypographic());
+        int formatFlags = Gdip.StringFormat_GetFormatFlags(format) | Gdip.StringFormatFlagsMeasureTrailingSpaces;
+        if ((data.style & DWT.MIRRORED) !is 0) formatFlags |= Gdip.StringFormatFlagsDirectionRightToLeft;
+        Gdip.StringFormat_SetFormatFlags(format, formatFlags);
+        float[] tabs = (flags & DWT.DRAW_TAB) !is 0 ? new float[]{measureSpace(data.gdipFont, format) * 8} : new float[1];
+        Gdip.StringFormat_SetTabStops(format, 0, tabs.length, tabs);
+        Gdip.StringFormat_SetHotkeyPrefix(format, (flags & DWT.DRAW_MNEMONIC) !is 0 ? Gdip.HotkeyPrefixShow : Gdip.HotkeyPrefixNone);
+        Gdip.Graphics_MeasureString(data.gdipGraphics, buffer, buffer.length, data.gdipFont, pt, format, bounds);
+        Gdip.StringFormat_delete(format);
+        return new Point(length is 0 ? 0 : Math.round(bounds.Width), Math.round(bounds.Height));
+    }
+    if (string.length () is 0) {
+        SIZE size = new SIZE();
+//      OS.GetTextExtentPoint32(handle, SPACE, SPACE.length(), size);
+        OS.GetTextExtentPoint32W(handle, new char [] {' '}, 1, size);
+        return new Point(0, size.cy);
+    }
+    RECT rect = new RECT();
+    TCHAR buffer = new TCHAR(getCodePage(), string, false);
+    int uFormat = OS.DT_LEFT | OS.DT_CALCRECT;
+    if ((flags & DWT.DRAW_DELIMITER) is 0) uFormat |= OS.DT_SINGLELINE;
+    if ((flags & DWT.DRAW_TAB) !is 0) uFormat |= OS.DT_EXPANDTABS;
+    if ((flags & DWT.DRAW_MNEMONIC) is 0) uFormat |= OS.DT_NOPREFIX;
+    OS.DrawText(handle, buffer, buffer.length(), rect, uFormat);
+    return new Point(rect.right, rect.bottom);
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the receiver
+ */
+public String toString () {
+    if (isDisposed()) return "GC {*DISPOSED*}";
+    return "GC {" + handle + "}";
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new graphics context.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>GC</code>. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ *
+ * @param drawable the Drawable for the receiver.
+ * @param data the data for the receiver.
+ *
+ * @return a new <code>GC</code>
+ */
+public static GC win32_new(Drawable drawable, GCData data) {
+    GC gc = new GC();
+    int hDC = drawable.internal_new_GC(data);
+    gc.device = data.device;
+    gc.init(drawable, data, hDC);
+    return gc;
+}
+
+/**
+ * Invokes platform specific functionality to wrap a graphics context.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>GC</code>. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ *
+ * @param hDC the Windows HDC.
+ * @param data the data for the receiver.
+ *
+ * @return a new <code>GC</code>
+ */
+public static GC win32_new(int hDC, GCData data) {
+    GC gc = new GC();
+    gc.device = data.device;
+    gc.init(null, data, hDC);
+    return gc;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/Image.d	Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,2140 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.graphics.Image;
+
+
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.DWTException;
+import dwt.internal.gdip.Gdip;
+import dwt.internal.win32.OS;
+
+import dwt.graphics.Color;
+import dwt.graphics.Device;
+import dwt.graphics.Drawable;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.ImageData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.graphics.Rectangle;
+import dwt.graphics.Resource;
+
+
+import dwt.dwthelper.InputStream;
+import dwt.dwthelper.utils;
+
+import tango.text.convert.Format;
+//import tango.stdc.string;
+//import tango.stdc.stringz;
+
+/**
+ * Instances of this class are graphics which have been prepared
+ * for display on a specific device. That is, they are ready
+ * to paint using methods such as <code>GC.drawImage()</code>
+ * and display on widgets with, for example, <code>Button.setImage()</code>.
+ * <p>
+ * If loaded from a file format that supports it, an
+ * <code>Image</code> may have transparency, meaning that certain
+ * pixels are specified as being transparent when drawn. Examples
+ * of file formats that support transparency are GIF and PNG.
+ * </p><p>
+ * There are two primary ways to use <code>Images</code>.
+ * The first is to load a graphic file from disk and create an
+ * <code>Image</code> from it. This is done using an <code>Image</code>
+ * constructor, for example:
+ * <pre>
+ *    Image i = new Image(device, "C:\\graphic.bmp");
+ * </pre>
+ * A graphic file may contain a color table specifying which
+ * colors the image was intended to possess. In the above example,
+ * these colors will be mapped to the closest available color in
+ * DWT. It is possible to get more control over the mapping of
+ * colors as the image is being created, using code of the form:
+ * <pre>
+ *    ImageData data = new ImageData("C:\\graphic.bmp");
+ *    RGB[] rgbs = data.getRGBs();
+ *    // At this point, rgbs contains specifications of all
+ *    // the colors contained within this image. You may
+ *    // allocate as many of these colors as you wish by
+ *    // using the Color constructor Color(RGB), then
+ *    // create the image:
+ *    Image i = new Image(device, data);
+ * </pre>
+ * <p>
+ * Applications which require even greater control over the image
+ * loading process should use the support provided in class
+ * <code>ImageLoader</code>.
+ * </p><p>
+ * Application code must explicitly invoke the <code>Image.dispose()</code>
+ * method to release the operating system resources managed by each instance
+ * when those instances are no longer required.
+ * </p>
+ *
+ * @see Color
+ * @see ImageData
+ * @see ImageLoader
+ */
+
+public final class Image : Resource, Drawable {
+
+    /**
+     * specifies whether the receiver is a bitmap or an icon
+     * (one of <code>DWT.BITMAP</code>, <code>DWT.ICON</code>)
+     * <p>
+     * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
+     * public API. It is marked public only so that it can be shared
+     * within the packages provided by DWT. It is not available on all
+     * platforms and should never be accessed from application code.
+     * </p>
+     */
+    public int type;
+
+    /**
+     * the handle to the OS image resource
+     * (Warning: This field is platform dependent)
+     * <p>
+     * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
+     * public API. It is marked public only so that it can be shared
+     * within the packages provided by DWT. It is not available on all
+     * platforms and should never be accessed from application code.
+     * </p>
+     */
+    public HGDIOBJ handle;
+
+    /**
+     * specifies the transparent pixel
+     */
+    int transparentPixel = -1;
+
+    /**
+     * the GC which is drawing on the image
+     */
+    GC memGC;
+
+    /**
+     * the alpha data for the image
+     */
+    byte[] alphaData;
+
+    /**
+     * the global alpha value to be used for every pixel
+     */
+    int alpha = -1;
+
+    /**
+     * the image data used to create this image if it is a
+     * icon. Used only in WinCE
+     */
+    ImageData data;
+
+    /**
+     * width of the image
+     */
+    int width = -1;
+
+    /**
+     * height of the image
+     */
+    int height = -1;
+
+    /**
+     * specifies the default scanline padding
+     */
+    static const int DEFAULT_SCANLINE_PAD = 4;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+this () {
+}
+
+/**
+ * Constructs an empty instance of this class with the
+ * specified width and height. The result may be drawn upon
+ * by creating a GC and using any of its drawing operations,
+ * as shown in the following example:
+ * <pre>
+ *    Image i = new Image(device, width, height);
+ *    GC gc = new GC(i);
+ *    gc.drawRectangle(0, 0, 50, 50);
+ *    gc.dispose();
+ * </pre>
+ * <p>
+ * Note: Some platforms may have a limitation on the size
+ * of image that can be created (size depends on width, height,
+ * and depth). For example, Windows 95, 98, and ME do not allow
+ * images larger than 16M.
+ * </p>
+ *
+ * @param device the device on which to create the image
+ * @param width the width of the new image
+ * @param height the height of the new image
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if either the width or height is negative or zero</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
+ * </ul>
+ */
+public this(Device device, int width, int height) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    init(device, width, height);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs a new instance of this class based on the
+ * provided image, with an appearance that varies depending
+ * on the value of the flag. The possible flag values are:
+ * <dl>
+ * <dt><b>IMAGE_COPY</b></dt>
+ * <dd>the result is an identical copy of srcImage</dd>
+ * <dt><b>IMAGE_DISABLE</b></dt>
+ * <dd>the result is a copy of srcImage which has a <em>disabled</em> look</dd>
+ * <dt><b>IMAGE_GRAY</b></dt>
+ * <dd>the result is a copy of srcImage which has a <em>gray scale</em> look</dd>
+ * </dl>
+ *
+ * @param device the device on which to create the image
+ * @param srcImage the image to use as the source
+ * @param flag the style, either <code>IMAGE_COPY</code>, <code>IMAGE_DISABLE</code> or <code>IMAGE_GRAY</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if srcImage is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the flag is not one of <code>IMAGE_COPY</code>, <code>IMAGE_DISABLE</code> or <code>IMAGE_GRAY</code></li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon, or is otherwise in an invalid state</li>
+ *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the image is not supported</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
+ * </ul>
+ */
+public this(Device device, Image srcImage, int flag) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    this.device = device;
+    if (srcImage is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (srcImage.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    Rectangle rect = srcImage.getBounds();
+    switch (flag) {
+        case DWT.IMAGE_COPY: {
+            this.type = srcImage.type;
+            switch (type) {
+                case DWT.BITMAP:
+                    /* Get the HDC for the device */
+                    auto hDC = device.internal_new_GC(null);
+
+                    /* Copy the bitmap */
+                    auto hdcSource = OS.CreateCompatibleDC(hDC);
+                    auto hdcDest = OS.CreateCompatibleDC(hDC);
+                    auto hOldSrc = OS.SelectObject(hdcSource, srcImage.handle);
+                    BITMAP bm;
+                    OS.GetObject(srcImage.handle, BITMAP.sizeof, &bm);
+                    handle = OS.CreateCompatibleBitmap(hdcSource, rect.width, bm.bmBits !is null ? -rect.height : rect.height);
+                    if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES);
+                    auto hOldDest = OS.SelectObject(hdcDest, handle);
+                    OS.BitBlt(hdcDest, 0, 0, rect.width, rect.height, hdcSource, 0, 0, OS.SRCCOPY);
+                    OS.SelectObject(hdcSource, hOldSrc);
+                    OS.SelectObject(hdcDest, hOldDest);
+                    OS.DeleteDC(hdcSource);
+                    OS.DeleteDC(hdcDest);
+
+                    /* Release the HDC for the device */
+                    device.internal_dispose_GC(hDC, null);
+
+                    transparentPixel = srcImage.transparentPixel;
+                    alpha = srcImage.alpha;
+                    if (srcImage.alphaData !is null) {
+                        alphaData = new byte[srcImage.alphaData.length];
+                        System.arraycopy(srcImage.alphaData, 0, alphaData, 0, alphaData.length);
+                    }
+                    break;
+                case DWT.ICON:
+                    if (OS.IsWinCE) {
+                        init(device, srcImage.data);
+                    } else {
+                        handle = OS.CopyImage(srcImage.handle, OS.IMAGE_ICON, rect.width, rect.height, 0);
+                        if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES);
+                    }
+                    break;
+                default:
+                    DWT.error(DWT.ERROR_INVALID_IMAGE);
+            }
+            if (device.tracking) device.new_Object(this);
+            return;
+        }
+        case DWT.IMAGE_DISABLE: {
+            ImageData data = srcImage.getImageData();
+            PaletteData palette = data.palette;
+            RGB[] rgbs = new RGB[3];
+            rgbs[0] = device.getSystemColor(DWT.COLOR_BLACK).getRGB();
+            rgbs[1] = device.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW).getRGB();
+            rgbs[2] = device.getSystemColor(DWT.COLOR_WIDGET_BACKGROUND).getRGB();
+            ImageData newData = new ImageData(rect.width, rect.height, 8, new PaletteData(rgbs));
+            newData.alpha = data.alpha;
+            newData.alphaData = data.alphaData;
+            newData.maskData = data.maskData;
+            newData.maskPad = data.maskPad;
+            if (data.transparentPixel !is -1) newData.transparentPixel = 0;
+
+            /* Convert the pixels. */
+            int[] scanline = new int[rect.width];
+            int[] maskScanline = null;
+            ImageData mask = null;
+            if (data.maskData !is null) mask = data.getTransparencyMask();
+            if (mask !is null) maskScanline = new int[rect.width];
+            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 y=0; y<rect.height; y++) {
+                int offset = y * newData.bytesPerLine;
+                data.getPixels(0, y, rect.width, scanline, 0);
+                if (mask !is null) mask.getPixels(0, y, rect.width, maskScanline, 0);
+                for (int x=0; x<rect.width; x++) {
+                    int pixel = scanline[x];
+                    if (!((data.transparentPixel !is -1 && pixel is data.transparentPixel) || (mask !is null && maskScanline[x] is 0))) {
+                        int red, green, blue;
+                        if (palette.isDirect) {
+                            red = pixel & redMask;
+                            red = (redShift < 0) ? red >>> -redShift : red << redShift;
+                            green = pixel & greenMask;
+                            green = (greenShift < 0) ? green >>> -greenShift : green << greenShift;
+                            blue = pixel & blueMask;
+                            blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift;
+                        } else {
+                            red = palette.colors[pixel].red;
+                            green = palette.colors[pixel].green;
+                            blue = palette.colors[pixel].blue;
+                        }
+                        int intensity = red * red + green * green + blue * blue;
+                        if (intensity < 98304) {
+                            newData.data[offset] = cast(byte)1;
+                        } else {
+                            newData.data[offset] = cast(byte)2;
+                        }
+                    }
+                    offset++;
+                }
+            }
+            init (device, newData);
+            if (device.tracking) device.new_Object(this);
+            return;
+        }
+        case DWT.IMAGE_GRAY: {
+            ImageData data = srcImage.getImageData();
+            PaletteData palette = data.palette;
+            ImageData newData = data;
+            if (!palette.isDirect) {
+                /* Convert the palette entries to gray. */
+                RGB [] rgbs = palette.getRGBs();
+                for (int i=0; i<rgbs.length; i++) {
+                    if (data.transparentPixel !is i) {
+                        RGB color = rgbs [i];
+                        int red = color.red;
+                        int green = color.green;
+                        int blue = color.blue;
+                        int intensity = (red+red+green+green+green+green+green+blue) >> 3;
+                        color.red = color.green = color.blue = intensity;
+                    }
+                }
+                newData.palette = new PaletteData(rgbs);
+            } else {
+                /* Create a 8 bit depth image data with a gray palette. */
+                RGB[] rgbs = new RGB[256];
+                for (int i=0; i<rgbs.length; i++) {
+                    rgbs[i] = new RGB(i, i, i);
+                }
+                newData = new ImageData(rect.width, rect.height, 8, new PaletteData(rgbs));
+                newData.alpha = data.alpha;
+                newData.alphaData = data.alphaData;
+                newData.maskData = data.maskData;
+                newData.maskPad = data.maskPad;
+                if (data.transparentPixel !is -1) newData.transparentPixel = 254;
+
+                /* Convert the pixels. */
+                int[] scanline = new int[rect.width];
+                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 y=0; y<rect.height; y++) {
+                    int offset = y * newData.bytesPerLine;
+                    data.getPixels(0, y, rect.width, scanline, 0);
+                    for (int x=0; x<rect.width; x++) {
+                        int pixel = scanline[x];
+                        if (pixel !is data.transparentPixel) {
+                            int red = pixel & redMask;
+                            red = (redShift < 0) ? red >>> -redShift : red << redShift;
+                            int green = pixel & greenMask;
+                            green = (greenShift < 0) ? green >>> -greenShift : green << greenShift;
+                            int blue = pixel & blueMask;
+                            blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift;
+                            int intensity = (red+red+green+green+green+green+green+blue) >> 3;
+                            if (newData.transparentPixel is intensity) intensity = 255;
+                            newData.data[offset] = cast(byte)intensity;
+                        } else {
+                            newData.data[offset] = cast(byte)254;
+                        }
+                        offset++;
+                    }
+                }
+            }
+            init (device, newData);
+            if (device.tracking) device.new_Object(this);
+            return;
+        }
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+}
+
+/**
+ * Constructs an empty instance of this class with the
+ * width and height of the specified rectangle. The result
+ * may be drawn upon by creating a GC and using any of its
+ * drawing operations, as shown in the following example:
+ * <pre>
+ *    Image i = new Image(device, boundsRectangle);
+ *    GC gc = new GC(i);
+ *    gc.drawRectangle(0, 0, 50, 50);
+ *    gc.dispose();
+ * </pre>
+ * <p>
+ * Note: Some platforms may have a limitation on the size
+ * of image that can be created (size depends on width, height,
+ * and depth). For example, Windows 95, 98, and ME do not allow
+ * images larger than 16M.
+ * </p>
+ *
+ * @param device the device on which to create the image
+ * @param bounds a rectangle specifying the image's width and height (must not be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the bounds rectangle is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if either the rectangle's width or height is negative</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
+ * </ul>
+ */
+public this(Device device, Rectangle bounds) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (bounds is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    init(device, bounds.width, bounds.height);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs an instance of this class from the given
+ * <code>ImageData</code>.
+ *
+ * @param device the device on which to create the image
+ * @param data the image data to create the image from (must not be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the image data is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the ImageData is not supported</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
+ * </ul>
+ */
+public this(Device device, ImageData data) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    init(device, data);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs an instance of this class, whose type is
+ * <code>DWT.ICON</code>, from the two given <code>ImageData</code>
+ * objects. The two images must be the same size. Pixel transparency
+ * in either image will be ignored.
+ * <p>
+ * The mask image should contain white wherever the icon is to be visible,
+ * and black wherever the icon is to be transparent. In addition,
+ * the source image should contain black wherever the icon is to be
+ * transparent.
+ * </p>
+ *
+ * @param device the device on which to create the icon
+ * @param source the color data for the icon
+ * @param mask the mask data for the icon
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if either the source or mask is null </li>
+ *    <li>ERROR_INVALID_ARGUMENT - if source and mask are different sizes</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
+ * </ul>
+ */
+public this(Device device, ImageData source, ImageData mask) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (source is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (mask is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (source.width !is mask.width || source.height !is mask.height) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    mask = ImageData.convertMask(mask);
+    init(device, this, source, mask);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs an instance of this class by loading its representation
+ * from the specified input stream. Throws an error if an error
+ * occurs while loading the image, or if the result is an image
+ * of an unsupported type.  Application code is still responsible
+ * for closing the input stream.
+ * <p>
+ * This constructor is provided for convenience when loading a single
+ * image only. If the stream contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * <code>ImageLoader.load()</code>.
+ * </p><p>
+ * This constructor may be used to load a resource as follows:
+ * </p>
+ * <pre>
+ *     static Image loadImage (Display display, Class clazz, String string) {
+ *          InputStream stream = clazz.getResourceAsStream (string);
+ *          if (stream is null) return null;
+ *          Image image = null;
+ *          try {
+ *               image = new Image (display, stream);
+ *          } catch (DWTException ex) {
+ *          } finally {
+ *               try {
+ *                    stream.close ();
+ *               } catch (IOException ex) {}
+ *          }
+ *          return image;
+ *     }
+ * </pre>
+ *
+ * @param device the device on which to create the image
+ * @param stream the input stream to load the image from
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data </li>
+ *    <li>ERROR_UNSUPPORTED_DEPTH - if the image stream describes an image with an unsupported depth</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
+ * </ul>
+ */
+public this (Device device, InputStream stream) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    init(device, new ImageData(stream));
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs an instance of this class by loading its representation
+ * from the file with the specified name. Throws an error if an error
+ * occurs while loading the image, or if the result is an image
+ * of an unsupported type.
+ * <p>
+ * This constructor is provided for convenience when loading
+ * a single image only. If the specified file contains
+ * multiple images, only the first one will be used.
+ *
+ * @param device the device on which to create the image
+ * @param filename the name of the file to load the image from
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the file</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data </li>
+ *    <li>ERROR_UNSUPPORTED_DEPTH - if the image file describes an image with an unsupported depth</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
+ * </ul>
+ */
+public this (Device device, char[] filename) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (filename is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    this.device = device;
+    try {
+        device.checkGDIP();
+        int length = filename.length;
+        char[] chars = new char[length+1];
+        filename.getChars(0, length, chars, 0);
+        auto bitmap = Gdip.Bitmap_new( .StrToWCHARz( filename ), false);
+        if (bitmap !is null) {
+            int error = DWT.ERROR_NO_HANDLES;
+            int status = Gdip.Image_GetLastStatus(cast(Gdip.Image*)bitmap);
+            if (status is 0) {
+                if (filename.toLowerCase().endsWith(".ico")) {
+                    this.type = DWT.ICON;
+                    HICON hicon;
+                    Gdip.Bitmap_GetHICON(bitmap, &hicon);
+                    this.handle = hicon;
+                } else {
+                    this.type = DWT.BITMAP;
+                    int width = Gdip.Image_GetWidth(cast(Gdip.Image*)bitmap);
+                    int height = Gdip.Image_GetHeight(cast(Gdip.Image*)bitmap);
+                    int pixelFormat = Gdip.Image_GetPixelFormat(cast(Gdip.Image*)bitmap);
+                    switch (pixelFormat) {
+                        case Gdip.PixelFormat16bppRGB555:
+                        case Gdip.PixelFormat16bppRGB565:
+                            this.handle = createDIB(width, height, 16);
+                            break;
+                        case Gdip.PixelFormat24bppRGB:
+                            this.handle = createDIB(width, height, 24);
+                            break;
+                        case Gdip.PixelFormat32bppRGB:
+                        // These will loose either precision or transparency
+                        case Gdip.PixelFormat16bppGrayScale:
+                        case Gdip.PixelFormat48bppRGB:
+                        case Gdip.PixelFormat32bppPARGB:
+                        case Gdip.PixelFormat64bppARGB:
+                        case Gdip.PixelFormat64bppPARGB:
+                            this.handle = createDIB(width, height, 32);
+                            break;
+                    }
+                    if (this.handle !is null) {
+                        /*
+                        * This performs better than getting the bits with Bitmap.LockBits(),
+                        * but it cannot be used when there is transparency.
+                        */
+                        auto hDC = device.internal_new_GC(null);
+                        auto srcHDC = OS.CreateCompatibleDC(hDC);
+                        auto oldSrcBitmap = OS.SelectObject(srcHDC, this.handle);
+                        auto graphics = Gdip.Graphics_new(srcHDC);
+                        if (graphics !is null) {
+                            Gdip.Rect rect;
+                            rect.Width = width;
+                            rect.Height = height;
+                            status = Gdip.Graphics_DrawImage(graphics, cast(Gdip.Image*)bitmap, &rect, 0, 0, width, height, Gdip.UnitPixel, null, null, null);
+                            if (status !is 0) {
+                                error = DWT.ERROR_INVALID_IMAGE;
+                                OS.DeleteObject(handle);
+                                this.handle = null;
+                            }
+                            Gdip.Graphics_delete(graphics);
+                        }
+                        OS.SelectObject(srcHDC, oldSrcBitmap);
+                        OS.DeleteDC(srcHDC);
+                        device.internal_dispose_GC(hDC, null);
+                    } else {
+                        auto lockedBitmapData = Gdip.BitmapData_new();
+                        if (lockedBitmapData !is null) {
+                            Gdip.Bitmap_LockBits(bitmap, null, 0, pixelFormat, lockedBitmapData);
+                            //BitmapData bitmapData = new BitmapData();
+                            //Gdip.MoveMemory(bitmapData, lockedBitmapData);
+                            auto stride = lockedBitmapData.Stride;
+                            auto pixels = lockedBitmapData.Scan0;
+                            int depth = 0, scanlinePad = 4, transparentPixel = -1;
+                            switch (lockedBitmapData.PixelFormat) {
+                                case Gdip.PixelFormat1bppIndexed: depth = 1; break;
+                                case Gdip.PixelFormat4bppIndexed: depth = 4; break;
+                                case Gdip.PixelFormat8bppIndexed: depth = 8; break;
+                                case Gdip.PixelFormat16bppARGB1555:
+                                case Gdip.PixelFormat16bppRGB555:
+                                case Gdip.PixelFormat16bppRGB565: depth = 16; break;
+                                case Gdip.PixelFormat24bppRGB: depth = 24; break;
+                                case Gdip.PixelFormat32bppRGB:
+                                case Gdip.PixelFormat32bppARGB: depth = 32; break;
+                            }
+                            if (depth !is 0) {
+                                PaletteData paletteData = null;
+                                switch (lockedBitmapData.PixelFormat) {
+                                    case Gdip.PixelFormat1bppIndexed:
+                                    case Gdip.PixelFormat4bppIndexed:
+                                    case Gdip.PixelFormat8bppIndexed:
+                                        int paletteSize = Gdip.Image_GetPaletteSize(cast(Gdip.Image*)bitmap);
+                                        auto hHeap = OS.GetProcessHeap();
+                                        auto palette = cast(Gdip.ColorPalette*) OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, paletteSize);
+                                        if (palette is null) DWT.error(DWT.ERROR_NO_HANDLES);
+                                        Gdip.Image_GetPalette(cast(Gdip.Image*)bitmap, palette, paletteSize);
+                                        Gdip.ColorPalette* colorPalette = palette;
+                                        //Gdip.MoveMemory(colorPalette, palette, ColorPalette.sizeof);
+                                        int[] entries = new int[colorPalette.Count];
+                                        //OS.MoveMemory(entries, palette + 8, entries.length * 4);
+                                        RGB[] rgbs = new RGB[colorPalette.Count];
+                                        paletteData = new PaletteData(rgbs);
+                                        for (int i = 0; i < entries.length; i++) {
+                                            if (((palette.Entries[i] >> 24) & 0xFF) is 0 && (colorPalette.Flags & Gdip.PaletteFlagsHasAlpha) !is 0) {
+                                                transparentPixel = i;
+                                            }
+                                            rgbs[i] = new RGB(((palette.Entries[i] & 0xFF0000) >> 16), ((palette.Entries[i] & 0xFF00) >> 8), ((palette.Entries[i] & 0xFF) >> 0));
+                                        }
+                                        OS.HeapFree(hHeap, 0, palette);
+                                        break;
+                                    case Gdip.PixelFormat16bppARGB1555:
+                                    case Gdip.PixelFormat16bppRGB555: paletteData = new PaletteData(0x7C00, 0x3E0, 0x1F); break;
+                                    case Gdip.PixelFormat16bppRGB565: paletteData = new PaletteData(0xF800, 0x7E0, 0x1F); break;
+                                    case Gdip.PixelFormat24bppRGB: paletteData = new PaletteData(0xFF, 0xFF00, 0xFF0000); break;
+                                    case Gdip.PixelFormat32bppRGB:
+                                    case Gdip.PixelFormat32bppARGB: paletteData = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); break;
+                                }
+                                byte[] data = (cast(byte*)data)[ 0 .. stride * height], alphaData = null;
+                                //OS.MoveMemory(data, pixels, data.length);
+                                switch (lockedBitmapData.PixelFormat) {
+                                    case Gdip.PixelFormat16bppARGB1555:
+                                        alphaData = new byte[width * height];
+                                        for (int i = 1, j = 0; i < data.length; i += 2, j++) {
+                                            alphaData[j] = cast(byte)((data[i] & 0x80) !is 0 ? 255 : 0);
+                                        }
+                                        break;
+                                    case Gdip.PixelFormat32bppARGB:
+                                        alphaData = new byte[width * height];
+                                        for (int i = 3, j = 0; i < data.length; i += 4, j++) {
+                                            alphaData[j] = data[i];
+                                        }
+                                        break;
+                                }
+                                Gdip.Bitmap_UnlockBits(bitmap, lockedBitmapData);
+                                Gdip.BitmapData_delete(lockedBitmapData);
+                                ImageData img = new ImageData(width, height, depth, paletteData, scanlinePad, data);
+                                img.transparentPixel = transparentPixel;
+                                img.alphaData = alphaData;
+                                init(device, img);
+                            }
+                        }
+                    }
+                }
+            }
+            Gdip.Bitmap_delete(bitmap);
+            if (status is 0) {
+                if (this.handle is null) DWT.error(error);
+                return;
+            }
+        }
+    } catch (DWTException e) {}
+    init(device, new ImageData(filename));
+    if(device.tracking) device.new_Object(this);
+}
+
+/**
+ * Create a DIB from a DDB without using GetDIBits. Note that
+ * the DDB should not be selected into a HDC.
+ */
+HBITMAP createDIBFromDDB(HDC hDC, HBITMAP hBitmap, int width, int height) {
+
+    /* Determine the DDB depth */
+    int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
+    int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
+    int depth = bits * planes;
+
+    /* Determine the DIB palette */
+    bool isDirect = depth > 8;
+    RGB[] rgbs = null;
+    if (!isDirect) {
+        int numColors = 1 << depth;
+        byte[] logPalette = new byte[4 * numColors];
+        OS.GetPaletteEntries(device.hPalette, 0, numColors, cast(PALETTEENTRY*)logPalette.ptr);
+        rgbs = new RGB[numColors];
+        for (int i = 0; i < numColors; i++) {
+            rgbs[i] = new RGB(logPalette[i] & 0xFF, logPalette[i + 1] & 0xFF, logPalette[i + 2] & 0xFF);
+        }
+    }
+
+    bool useBitfields = OS.IsWinCE && (depth is 16 || depth is 32);
+    BITMAPINFOHEADER bmiHeader;
+    bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+    bmiHeader.biWidth = width;
+    bmiHeader.biHeight = -height;
+    bmiHeader.biPlanes = 1;
+    bmiHeader.biBitCount = cast(short)depth;
+    if (useBitfields) bmiHeader.biCompression = OS.BI_BITFIELDS;
+    else bmiHeader.biCompression = OS.BI_RGB;
+    byte[] bmi;
+    if (isDirect) bmi = new byte[BITMAPINFOHEADER.sizeof + (useBitfields ? 12 : 0)];
+    else  bmi = new byte[BITMAPINFOHEADER.sizeof + rgbs.length * 4];
+    bmi[ 0 .. BITMAPINFOHEADER.sizeof ] = (cast(byte*)&bmiHeader)[ 0 .. BITMAPINFOHEADER.sizeof ];
+    //OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+
+    /* Set the rgb colors into the bitmap info */
+    int offset = BITMAPINFOHEADER.sizeof;
+    if (isDirect) {
+        if (useBitfields) {
+            int redMask = 0;
+            int greenMask = 0;
+            int blueMask = 0;
+            switch (depth) {
+                case 16:
+                    redMask = 0x7C00;
+                    greenMask = 0x3E0;
+                    blueMask = 0x1F;
+                    /* little endian */
+                    bmi[offset] = cast(byte)((redMask & 0xFF) >> 0);
+                    bmi[offset + 1] = cast(byte)((redMask & 0xFF00) >> 8);
+                    bmi[offset + 2] = cast(byte)((redMask & 0xFF0000) >> 16);
+                    bmi[offset + 3] = cast(byte)((redMask & 0xFF000000) >> 24);
+                    bmi[offset + 4] = cast(byte)((greenMask & 0xFF) >> 0);
+                    bmi[offset + 5] = cast(byte)((greenMask & 0xFF00) >> 8);
+                    bmi[offset + 6] = cast(byte)((greenMask & 0xFF0000) >> 16);
+                    bmi[offset + 7] = cast(byte)((greenMask & 0xFF000000) >> 24);
+                    bmi[offset + 8] = cast(byte)((blueMask & 0xFF) >> 0);
+                    bmi[offset + 9] = cast(byte)((blueMask & 0xFF00) >> 8);
+                    bmi[offset + 10] = cast(byte)((blueMask & 0xFF0000) >> 16);
+                    bmi[offset + 11] = cast(byte)((blueMask & 0xFF000000) >> 24);
+                    break;
+                case 32:
+                    redMask = 0xFF00;
+                    greenMask = 0xFF0000;
+                    blueMask = 0xFF000000;
+                    /* big endian */
+                    bmi[offset] = cast(byte)((redMask & 0xFF000000) >> 24);
+                    bmi[offset + 1] = cast(byte)((redMask & 0xFF0000) >> 16);
+                    bmi[offset + 2] = cast(byte)((redMask & 0xFF00) >> 8);
+                    bmi[offset + 3] = cast(byte)((redMask & 0xFF) >> 0);
+                    bmi[offset + 4] = cast(byte)((greenMask & 0xFF000000) >> 24);
+                    bmi[offset + 5] = cast(byte)((greenMask & 0xFF0000) >> 16);
+                    bmi[offset + 6] = cast(byte)((greenMask & 0xFF00) >> 8);
+                    bmi[offset + 7] = cast(byte)((greenMask & 0xFF) >> 0);
+                    bmi[offset + 8] = cast(byte)((blueMask & 0xFF000000) >> 24);
+                    bmi[offset + 9] = cast(byte)((blueMask & 0xFF0000) >> 16);
+                    bmi[offset + 10] = cast(byte)((blueMask & 0xFF00) >> 8);
+                    bmi[offset + 11] = cast(byte)((blueMask & 0xFF) >> 0);
+                    break;
+                default:
+                    DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+            }
+        }
+    } else {
+        for (int j = 0; j < rgbs.length; j++) {
+            bmi[offset] = cast(byte)rgbs[j].blue;
+            bmi[offset + 1] = cast(byte)rgbs[j].green;
+            bmi[offset + 2] = cast(byte)rgbs[j].red;
+            bmi[offset + 3] = 0;
+            offset += 4;
+        }
+    }
+    int pBits;
+    HBITMAP hDib = OS.CreateDIBSection(null, cast(BITMAPINFO*)bmi.ptr, OS.DIB_RGB_COLORS, &pBits, null, 0);
+    if (hDib is null) DWT.error(DWT.ERROR_NO_HANDLES);
+
+    /* Bitblt DDB into DIB */
+    auto hdcSource = OS.CreateCompatibleDC(hDC);
+    auto hdcDest = OS.CreateCompatibleDC(hDC);
+    auto hOldSrc = OS.SelectObject(hdcSource, hBitmap);
+    auto hOldDest = OS.SelectObject(hdcDest, hDib);
+    OS.BitBlt(hdcDest, 0, 0, width, height, hdcSource, 0, 0, OS.SRCCOPY);
+    OS.SelectObject(hdcSource, hOldSrc);
+    OS.SelectObject(hdcDest, hOldDest);
+    OS.DeleteDC(hdcSource);
+    OS.DeleteDC(hdcDest);
+
+    return hDib;
+}
+
+int[] createGdipImage() {
+    switch (type) {
+        case DWT.BITMAP: {
+            if (alpha !is -1 || alphaData !is null || transparentPixel !is -1) {
+                BITMAP bm;
+                OS.GetObject(handle, BITMAP.sizeof, &bm);
+                int imgWidth = bm.bmWidth;
+                int imgHeight = bm.bmHeight;
+                auto hDC = device.internal_new_GC(null);
+                auto srcHdc = OS.CreateCompatibleDC(hDC);
+                auto oldSrcBitmap = OS.SelectObject(srcHdc, handle);
+                auto memHdc = OS.CreateCompatibleDC(hDC);
+                auto memDib = createDIB(imgWidth, imgHeight, 32);
+                if (memDib is null) DWT.error(DWT.ERROR_NO_HANDLES);
+                auto oldMemBitmap = OS.SelectObject(memHdc, memDib);
+                BITMAP dibBM;
+                OS.GetObject(memDib, BITMAP.sizeof, &dibBM);
+                int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
+                OS.BitBlt(memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY);
+                byte red = 0, green = 0, blue = 0;
+                if (transparentPixel !is -1) {
+                    if (bm.bmBitsPixel <= 8)  {
+                        byte[] color = new byte[4];
+                        OS.GetDIBColorTable(srcHdc, transparentPixel, 1, cast(RGBQUAD*)color.ptr);
+                        blue = color[0];
+                        green = color[1];
+                        red = color[2];
+                    } else {
+                        switch (bm.bmBitsPixel) {
+                            case 16:
+                                int blueMask = 0x1F;
+                                int blueShift = ImageData.getChannelShift(blueMask);
+                                byte[] blues = ImageData.ANY_TO_EIGHT[ImageData.getChannelWidth(blueMask, blueShift)];
+                                blue = blues[(transparentPixel & blueMask) >> blueShift];
+                                int greenMask = 0x3E0;
+                                int greenShift = ImageData.getChannelShift(greenMask);
+                                byte[] greens = ImageData.ANY_TO_EIGHT[ImageData.getChannelWidth(greenMask, greenShift)];
+                                green = greens[(transparentPixel & greenMask) >> greenShift];
+                                int redMask = 0x7C00;
+                                int redShift = ImageData.getChannelShift(redMask);
+                                byte[] reds = ImageData.ANY_TO_EIGHT[ImageData.getChannelWidth(redMask, redShift)];
+                                red = reds[(transparentPixel & redMask) >> redShift];
+                                break;
+                            case 24:
+                                blue = cast(byte)((transparentPixel & 0xFF0000) >> 16);
+                                green = cast(byte)((transparentPixel & 0xFF00) >> 8);
+                                red = cast(byte)(transparentPixel & 0xFF);
+                                break;
+                            case 32:
+                                blue = cast(byte)((transparentPixel & 0xFF000000) >>> 24);
+                                green = cast(byte)((transparentPixel & 0xFF0000) >> 16);
+                                red = cast(byte)((transparentPixel & 0xFF00) >> 8);
+                                break;
+                        }
+                    }
+                }
+                OS.SelectObject(srcHdc, oldSrcBitmap);
+                OS.SelectObject(memHdc, oldMemBitmap);
+                OS.DeleteObject(srcHdc);
+                OS.DeleteObject(memHdc);
+                byte[] srcData = (cast(byte*)dibBM.bmBits)[ 0 .. sizeInBytes ].dup;
+                OS.DeleteObject(memDib);
+                device.internal_dispose_GC(hDC, null);
+                if (alpha !is -1) {
+                    for (int y = 0, dp = 0; y < imgHeight; ++y) {
+                        for (int x = 0; x < imgWidth; ++x) {
+                            srcData[dp + 3] = cast(byte)alpha;
+                            dp += 4;
+                        }
+                    }
+                } else if (alphaData !is null) {
+                    for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) {
+                        for (int x = 0; x < imgWidth; ++x) {
+                            srcData[dp + 3] = alphaData[ap++];
+                            dp += 4;
+                        }
+                    }
+                } else if (transparentPixel !is -1) {
+                    for (int y = 0, dp = 0; y < imgHeight; ++y) {
+                        for (int x = 0; x < imgWidth; ++x) {
+                            if (srcData[dp] is blue && srcData[dp + 1] is green && srcData[dp + 2] is red) {
+                                srcData[dp + 3] = cast(byte)0;
+                            } else {
+                                srcData[dp + 3] = cast(byte)0xFF;
+                            }
+                            dp += 4;
+                        }
+                    }
+                }
+                auto hHeap = OS.GetProcessHeap();
+                auto pixels = cast(byte*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, srcData.length);
+                if (pixels is null) DWT.error(DWT.ERROR_NO_HANDLES);
+                OS.MoveMemory(pixels, srcData.ptr, sizeInBytes);
+                return [ cast(int)Gdip.Bitmap_new(imgWidth, imgHeight, dibBM.bmWidthBytes, Gdip.PixelFormat32bppARGB, pixels), cast(int) pixels];
+            }
+            return [cast(int)Gdip.Bitmap_new(handle, null), 0];
+        }
+        case DWT.ICON: {
+            /*
+            * Bug in GDI+. Creating a new GDI+ Bitmap from a HICON segment faults
+            * when the icon width is bigger than the icon height.  The fix is to
+            * detect this and create a PixelFormat32bppARGB image instead.
+            */
+            ICONINFO iconInfo;
+            if (OS.IsWinCE) {
+                GetIconInfo(this, &iconInfo);
+            } else {
+                OS.GetIconInfo(handle, &iconInfo);
+            }
+            auto hBitmap = iconInfo.hbmColor;
+            if (hBitmap is null) hBitmap = iconInfo.hbmMask;
+            BITMAP bm;
+            OS.GetObject(hBitmap, BITMAP.sizeof, &bm);
+            int imgWidth = bm.bmWidth;
+            int imgHeight = hBitmap is iconInfo.hbmMask ? bm.bmHeight / 2 : bm.bmHeight;
+            int img = 0, pixels = 0;
+            if (imgWidth > imgHeight) {
+                auto hDC = device.internal_new_GC(null);
+                auto srcHdc = OS.CreateCompatibleDC(hDC);
+                auto oldSrcBitmap = OS.SelectObject(srcHdc, hBitmap);
+                auto memHdc = OS.CreateCompatibleDC(hDC);
+                auto memDib = createDIB(imgWidth, imgHeight, 32);
+                if (memDib is null) DWT.error(DWT.ERROR_NO_HANDLES);
+                auto oldMemBitmap = OS.SelectObject(memHdc, memDib);
+                BITMAP dibBM;
+                OS.GetObject(memDib, BITMAP.sizeof, &dibBM);
+                OS.BitBlt(memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, hBitmap is iconInfo.hbmMask ? imgHeight : 0, OS.SRCCOPY);
+                OS.SelectObject(memHdc, oldMemBitmap);
+                OS.DeleteObject(memHdc);
+                byte[] srcData = new byte[dibBM.bmWidthBytes * dibBM.bmHeight];
+                OS.MoveMemory(srcData, dibBM.bmBits, srcData.length);
+                OS.DeleteObject(memDib);
+                OS.SelectObject(srcHdc, iconInfo.hbmMask);
+                for (int y = 0, dp = 0; y < imgHeight; ++y) {
+                    for (int x = 0; x < imgWidth; ++x) {
+                        if (OS.GetPixel(srcHdc, x, y) !is 0) {
+                            srcData[dp + 3] = cast(byte)0;
+                        } else {
+                            srcData[dp + 3] = cast(byte)0xFF;
+                        }
+                        dp += 4;
+                    }
+                }
+                OS.SelectObject(srcHdc, oldSrcBitmap);
+                OS.DeleteObject(srcHdc);
+                device.internal_dispose_GC(hDC, null);
+                int hHeap = OS.GetProcessHeap();
+                pixels = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, srcData.length);
+                if (pixels is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                OS.MoveMemory(pixels, srcData, srcData.length);
+                img = Gdip.Bitmap_new(imgWidth, imgHeight, dibBM.bmWidthBytes, Gdip.PixelFormat32bppARGB, pixels);
+            } else {
+                img = Gdip.Bitmap_new(handle);
+            }
+            if (iconInfo.hbmColor !is 0) OS.DeleteObject(iconInfo.hbmColor);
+            if (iconInfo.hbmMask !is 0) OS.DeleteObject(iconInfo.hbmMask);
+            return [img, pixels];
+        }
+        default: DWT.error(DWT.ERROR_INVALID_IMAGE);
+    }
+    return null;
+}
+
+/**
+ * Disposes of the operating system resources associated with
+ * the image. Applications must dispose of all images which
+ * they allocate.
+ */
+public void dispose () {
+    if (handle is null) return;
+    if (device.isDisposed()) return;
+    if (memGC !is null) memGC.dispose();
+    if (type is DWT.ICON) {
+        if (OS.IsWinCE) data = null;
+        OS.DestroyIcon (handle);
+    } else {
+        OS.DeleteObject (handle);
+    }
+    handle = null;
+    memGC = null;
+    if (device.tracking) device.dispose_Object(this);
+    device = null;
+}
+
+/**
+ * Compares the argument to the receiver, and returns true
+ * if they represent the <em>same</em> object using a class
+ * specific comparison.
+ *
+ * @param object the object to compare with this object
+ * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
+ *
+ * @see #hashCode
+ */
+public int opEquals (Object object) {
+    if (object is this) return true;
+    if (!(cast(Image)object)) return false;
+    Image image = cast(Image) object;
+    return device is image.device && handle is image.handle;
+}
+
+/**
+ * Returns the color to which to map the transparent pixel, or null if
+ * the receiver has no transparent pixel.
+ * <p>
+ * There are certain uses of Images that do not support transparency
+ * (for example, setting an image into a button or label). In these cases,
+ * it may be desired to simulate transparency by using the background
+ * color of the widget to paint the transparent pixels of the image.
+ * Use this method to check which color will be used in these cases
+ * in place of transparency. This value may be set with setBackground().
+ * <p>
+ *
+ * @return the background color of the image, or null if there is no transparency in the image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Color getBackground() {
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (transparentPixel is -1) return null;
+
+    /* Get the HDC for the device */
+    auto hDC = device.internal_new_GC(null);
+
+    /* Compute the background color */
+    BITMAP bm = new BITMAP();
+    OS.GetObject(handle, BITMAP.sizeof, bm);
+    auto hdcMem = OS.CreateCompatibleDC(hDC);
+    auto hOldObject = OS.SelectObject(hdcMem, handle);
+    int red = 0, green = 0, blue = 0;
+    if (bm.bmBitsPixel <= 8)  {
+        if (OS.IsWinCE) {
+            byte[] pBits = new byte[1];
+            OS.MoveMemory(pBits, bm.bmBits, 1);
+            byte oldValue = pBits[0];
+            int mask = (0xFF << (8 - bm.bmBitsPixel)) & 0x00FF;
+            pBits[0] = cast(byte)((transparentPixel << (8 - bm.bmBitsPixel)) | (pBits[0] & ~mask));
+            OS.MoveMemory(bm.bmBits, pBits, 1);
+            int color = OS.GetPixel(hdcMem, 0, 0);
+            pBits[0] = oldValue;
+            OS.MoveMemory(bm.bmBits, pBits, 1);
+            blue = (color & 0xFF0000) >> 16;
+            green = (color & 0xFF00) >> 8;
+            red = color & 0xFF;
+        } else {
+            byte[] color = new byte[4];
+            OS.GetDIBColorTable(hdcMem, transparentPixel, 1, color);
+            blue = color[0] & 0xFF;
+            green = color[1] & 0xFF;
+            red = color[2] & 0xFF;
+        }
+    } else {
+        switch (bm.bmBitsPixel) {
+            case 16:
+                blue = (transparentPixel & 0x1F) << 3;
+                green = (transparentPixel & 0x3E0) >> 2;
+                red = (transparentPixel & 0x7C00) >> 7;
+                break;
+            case 24:
+                blue = (transparentPixel & 0xFF0000) >> 16;
+                green = (transparentPixel & 0xFF00) >> 8;
+                red = transparentPixel & 0xFF;
+                break;
+            case 32:
+                blue = (transparentPixel & 0xFF000000) >>> 24;
+                green = (transparentPixel & 0xFF0000) >> 16;
+                red = (transparentPixel & 0xFF00) >> 8;
+                break;
+            default:
+                return null;
+        }
+    }
+    OS.SelectObject(hdcMem, hOldObject);
+    OS.DeleteDC(hdcMem);
+
+    /* Release the HDC for the device */
+    device.internal_dispose_GC(hDC, null);
+    return Color.win32_new(device, (blue << 16) | (green << 8) | red);
+}
+
+/**
+ * Returns the bounds of the receiver. The rectangle will always
+ * have x and y values of 0, and the width and height of the
+ * image.
+ *
+ * @return a rectangle specifying the image's bounds
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li>
+ * </ul>
+ */
+public Rectangle getBounds() {
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (width !is -1 && height !is -1) {
+        return new Rectangle(0, 0, width, height);
+    }
+    switch (type) {
+        case DWT.BITMAP:
+            BITMAP bm = new BITMAP();
+            OS.GetObject(handle, BITMAP.sizeof, bm);
+            return new Rectangle(0, 0, width = bm.bmWidth, height = bm.bmHeight);
+        case DWT.ICON:
+            if (OS.IsWinCE) {
+                return new Rectangle(0, 0, width = data.width, height = data.height);
+            } else {
+                ICONINFO info = new ICONINFO();
+                OS.GetIconInfo(handle, info);
+                int hBitmap = info.hbmColor;
+                if (hBitmap is 0) hBitmap = info.hbmMask;
+                bm = new BITMAP();
+                OS.GetObject(hBitmap, BITMAP.sizeof, bm);
+                if (hBitmap is info.hbmMask) bm.bmHeight /= 2;
+                if (info.hbmColor !is 0) OS.DeleteObject(info.hbmColor);
+                if (info.hbmMask !is 0) OS.DeleteObject(info.hbmMask);
+                return new Rectangle(0, 0, width = bm.bmWidth, height = bm.bmHeight);
+            }
+        default:
+            DWT.error(DWT.ERROR_INVALID_IMAGE);
+            return null;
+    }
+}
+
+/**
+ * Returns an <code>ImageData</code> based on the receiver
+ * Modifications made to this <code>ImageData</code> will not
+ * affect the Image.
+ *
+ * @return an <code>ImageData</code> containing the image's data and attributes
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li>
+ * </ul>
+ *
+ * @see ImageData
+ */
+public ImageData getImageData() {
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    BITMAP bm;
+    int depth, width, height;
+    switch (type) {
+        case DWT.ICON: {
+            if (OS.IsWinCE) return data;
+            ICONINFO info = new ICONINFO();
+            if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+            OS.GetIconInfo(handle, info);
+            /* Get the basic BITMAP information */
+            int hBitmap = info.hbmColor;
+            if (hBitmap is 0) hBitmap = info.hbmMask;
+            bm = new BITMAP();
+            OS.GetObject(hBitmap, BITMAP.sizeof, bm);
+            depth = bm.bmPlanes * bm.bmBitsPixel;
+            width = bm.bmWidth;
+            if (hBitmap is info.hbmMask) bm.bmHeight /= 2;
+            height = bm.bmHeight;
+            int numColors = 0;
+            if (depth <= 8) numColors = 1 << depth;
+            /* Create the BITMAPINFO */
+            BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
+            bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+            bmiHeader.biWidth = width;
+            bmiHeader.biHeight = -height;
+            bmiHeader.biPlanes = 1;
+            bmiHeader.biBitCount = cast(short)depth;
+            bmiHeader.biCompression = OS.BI_RGB;
+            byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + numColors * 4];
+            OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+
+            /* Get the HDC for the device */
+            auto hDC = device.internal_new_GC(null);
+
+            /* Create the DC and select the bitmap */
+            auto hBitmapDC = OS.CreateCompatibleDC(hDC);
+            auto hOldBitmap = OS.SelectObject(hBitmapDC, hBitmap);
+            /* Select the palette if necessary */
+            int oldPalette = 0;
+            if (depth <= 8) {
+                int hPalette = device.hPalette;
+                if (hPalette !is 0) {
+                    oldPalette = OS.SelectPalette(hBitmapDC, hPalette, false);
+                    OS.RealizePalette(hBitmapDC);
+                }
+            }
+            /* Find the size of the image and allocate data */
+            int imageSize;
+            /* Call with null lpBits to get the image size */
+            if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+            OS.GetDIBits(hBitmapDC, hBitmap, 0, height, 0, bmi, OS.DIB_RGB_COLORS);
+            OS.MoveMemory(bmiHeader, bmi, BITMAPINFOHEADER.sizeof);
+            imageSize = bmiHeader.biSizeImage;
+            byte[] data = new byte[imageSize];
+            /* Get the bitmap data */
+            int hHeap = OS.GetProcessHeap();
+            int lpvBits = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, imageSize);
+            if (lpvBits is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+            OS.GetDIBits(hBitmapDC, hBitmap, 0, height, lpvBits, bmi, OS.DIB_RGB_COLORS);
+            OS.MoveMemory(data, lpvBits, imageSize);
+            /* Calculate the palette */
+            PaletteData palette = null;
+            if (depth <= 8) {
+                RGB[] rgbs = new RGB[numColors];
+                int srcIndex = 40;
+                for (int i = 0; i < numColors; i++) {
+                    rgbs[i] = new RGB(bmi[srcIndex + 2] & 0xFF, bmi[srcIndex + 1] & 0xFF, bmi[srcIndex] & 0xFF);
+                    srcIndex += 4;
+                }
+                palette = new PaletteData(rgbs);
+            } else if (depth is 16) {
+                palette = new PaletteData(0x7C00, 0x3E0, 0x1F);
+            } else if (depth is 24) {
+                palette = new PaletteData(0xFF, 0xFF00, 0xFF0000);
+            } else if (depth is 32) {
+                palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
+            } else {
+                DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+            }
+
+            /* Do the mask */
+            byte [] maskData = null;
+            if (info.hbmColor is 0) {
+                /* Do the bottom half of the mask */
+                maskData = new byte[imageSize];
+                if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+                OS.GetDIBits(hBitmapDC, hBitmap, height, height, lpvBits, bmi, OS.DIB_RGB_COLORS);
+                OS.MoveMemory(maskData, lpvBits, imageSize);
+            } else {
+                /* Do the entire mask */
+                /* Create the BITMAPINFO */
+                bmiHeader = new BITMAPINFOHEADER();
+                bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+                bmiHeader.biWidth = width;
+                bmiHeader.biHeight = -height;
+                bmiHeader.biPlanes = 1;
+                bmiHeader.biBitCount = 1;
+                bmiHeader.biCompression = OS.BI_RGB;
+                bmi = new byte[BITMAPINFOHEADER.sizeof + 8];
+                OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+
+                /* First color black, second color white */
+                int offset = BITMAPINFOHEADER.sizeof;
+                bmi[offset + 4] = bmi[offset + 5] = bmi[offset + 6] = cast(byte)0xFF;
+                bmi[offset + 7] = 0;
+                OS.SelectObject(hBitmapDC, info.hbmMask);
+                /* Call with null lpBits to get the image size */
+                if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+                OS.GetDIBits(hBitmapDC, info.hbmMask, 0, height, 0, bmi, OS.DIB_RGB_COLORS);
+                OS.MoveMemory(bmiHeader, bmi, BITMAPINFOHEADER.sizeof);
+                imageSize = bmiHeader.biSizeImage;
+                maskData = new byte[imageSize];
+                int lpvMaskBits = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, imageSize);
+                if (lpvMaskBits is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+                OS.GetDIBits(hBitmapDC, info.hbmMask, 0, height, lpvMaskBits, bmi, OS.DIB_RGB_COLORS);
+                OS.MoveMemory(maskData, lpvMaskBits, imageSize);
+                OS.HeapFree(hHeap, 0, lpvMaskBits);
+                /* Loop to invert the mask */
+                for (int i = 0; i < maskData.length; i++) {
+                    maskData[i] ^= -1;
+                }
+                /* Make sure mask scanlinePad is 2 */
+                int maskPad;
+                int bpl = imageSize / height;
+                for (maskPad = 1; maskPad < 128; maskPad++) {
+                    int calcBpl = (((width + 7) / 8) + (maskPad - 1)) / maskPad * maskPad;
+                    if (calcBpl is bpl) break;
+                }
+                maskData = ImageData.convertPad(maskData, width, height, 1, maskPad, 2);
+            }
+            /* Clean up */
+            OS.HeapFree(hHeap, 0, lpvBits);
+            OS.SelectObject(hBitmapDC, hOldBitmap);
+            if (oldPalette !is 0) {
+                OS.SelectPalette(hBitmapDC, oldPalette, false);
+                OS.RealizePalette(hBitmapDC);
+            }
+            OS.DeleteDC(hBitmapDC);
+
+            /* Release the HDC for the device */
+            device.internal_dispose_GC(hDC, null);
+
+            if (info.hbmColor !is 0) OS.DeleteObject(info.hbmColor);
+            if (info.hbmMask !is 0) OS.DeleteObject(info.hbmMask);
+            /* Construct and return the ImageData */
+            ImageData imageData = new ImageData(width, height, depth, palette, 4, data);
+            imageData.maskData = maskData;
+            imageData.maskPad = 2;
+            return imageData;
+        }
+        case DWT.BITMAP: {
+            /* Get the basic BITMAP information */
+            bm = new BITMAP();
+            OS.GetObject(handle, BITMAP.sizeof, bm);
+            depth = bm.bmPlanes * bm.bmBitsPixel;
+            width = bm.bmWidth;
+            height = bm.bmHeight;
+            /* Find out whether this is a DIB or a DDB. */
+            bool isDib = (bm.bmBits !is 0);
+            /* Get the HDC for the device */
+            auto hDC = device.internal_new_GC(null);
+
+            /*
+            * Feature in WinCE.  GetDIBits is not available in WinCE.  The
+            * workaround is to create a temporary DIB from the DDB and use
+            * the bmBits field of DIBSECTION to retrieve the image data.
+            */
+            int handle = this.handle;
+            if (OS.IsWinCE) {
+                if (!isDib) {
+                    bool mustRestore = false;
+                    if (memGC !is null && !memGC.isDisposed()) {
+                        memGC.flush ();
+                        mustRestore = true;
+                        GCData data = memGC.data;
+                        if (data.hNullBitmap !is 0) {
+                            OS.SelectObject(memGC.handle, data.hNullBitmap);
+                            data.hNullBitmap = 0;
+                        }
+                    }
+                    handle = createDIBFromDDB(hDC, this.handle, width, height);
+                    if (mustRestore) {
+                        auto hOldBitmap = OS.SelectObject(memGC.handle, this.handle);
+                        memGC.data.hNullBitmap = hOldBitmap;
+                    }
+                    isDib = true;
+                }
+            }
+            DIBSECTION dib = null;
+            if (isDib) {
+                dib = new DIBSECTION();
+                OS.GetObject(handle, DIBSECTION.sizeof, dib);
+            }
+            /* Calculate number of colors */
+            int numColors = 0;
+            if (depth <= 8) {
+                if (isDib) {
+                    numColors = dib.biClrUsed;
+                } else {
+                    numColors = 1 << depth;
+                }
+            }
+            /* Create the BITMAPINFO */
+            byte[] bmi = null;
+            BITMAPINFOHEADER bmiHeader = null;
+            if (!isDib) {
+                bmiHeader = new BITMAPINFOHEADER();
+                bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+                bmiHeader.biWidth = width;
+                bmiHeader.biHeight = -height;
+                bmiHeader.biPlanes = 1;
+                bmiHeader.biBitCount = cast(short)depth;
+                bmiHeader.biCompression = OS.BI_RGB;
+                bmi = new byte[BITMAPINFOHEADER.sizeof + numColors * 4];
+                OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+            }
+
+            /* Create the DC and select the bitmap */
+            auto hBitmapDC = OS.CreateCompatibleDC(hDC);
+            auto hOldBitmap = OS.SelectObject(hBitmapDC, handle);
+            /* Select the palette if necessary */
+            int oldPalette = 0;
+            if (!isDib && depth <= 8) {
+                int hPalette = device.hPalette;
+                if (hPalette !is 0) {
+                    oldPalette = OS.SelectPalette(hBitmapDC, hPalette, false);
+                    OS.RealizePalette(hBitmapDC);
+                }
+            }
+            /* Find the size of the image and allocate data */
+            int imageSize;
+            if (isDib) {
+                imageSize = dib.biSizeImage;
+            } else {
+                /* Call with null lpBits to get the image size */
+                if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+                OS.GetDIBits(hBitmapDC, handle, 0, height, 0, bmi, OS.DIB_RGB_COLORS);
+                OS.MoveMemory(bmiHeader, bmi, BITMAPINFOHEADER.sizeof);
+                imageSize = bmiHeader.biSizeImage;
+            }
+            byte[] data = new byte[imageSize];
+            /* Get the bitmap data */
+            if (isDib) {
+                if (OS.IsWinCE && this.handle !is handle) {
+                    /* get image data from the temporary DIB */
+                    OS.MoveMemory(data, dib.bmBits, imageSize);
+                } else {
+                    OS.MoveMemory(data, bm.bmBits, imageSize);
+                }
+            } else {
+                int hHeap = OS.GetProcessHeap();
+                int lpvBits = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, imageSize);
+                if (lpvBits is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+                if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+                OS.GetDIBits(hBitmapDC, handle, 0, height, lpvBits, bmi, OS.DIB_RGB_COLORS);
+                OS.MoveMemory(data, lpvBits, imageSize);
+                OS.HeapFree(hHeap, 0, lpvBits);
+            }
+            /* Calculate the palette */
+            PaletteData palette = null;
+            if (depth <= 8) {
+                RGB[] rgbs = new RGB[numColors];
+                if (isDib) {
+                    if (OS.IsWinCE) {
+                        /*
+                        * Feature on WinCE.  GetDIBColorTable is not supported.
+                        * The workaround is to set a pixel to the desired
+                        * palette index and use getPixel to get the corresponding
+                        * RGB value.
+                        */
+                        int red = 0, green = 0, blue = 0;
+                        byte[] pBits = new byte[1];
+                        OS.MoveMemory(pBits, bm.bmBits, 1);
+                        byte oldValue = pBits[0];
+                        int mask = (0xFF << (8 - bm.bmBitsPixel)) & 0x00FF;
+                        for (int i = 0; i < numColors; i++) {
+                            pBits[0] = cast(byte)((i << (8 - bm.bmBitsPixel)) | (pBits[0] & ~mask));
+                            OS.MoveMemory(bm.bmBits, pBits, 1);
+                            int color = OS.GetPixel(hBitmapDC, 0, 0);
+                            blue = (color & 0xFF0000) >> 16;
+                            green = (color & 0xFF00) >> 8;
+                            red = color & 0xFF;
+                            rgbs[i] = new RGB(red, green, blue);
+                        }
+                        pBits[0] = oldValue;
+                        OS.MoveMemory(bm.bmBits, pBits, 1);
+                    } else {
+                        byte[] colors = new byte[numColors * 4];
+                        OS.GetDIBColorTable(hBitmapDC, 0, numColors, colors);
+                        int colorIndex = 0;
+                        for (int i = 0; i < rgbs.length; i++) {
+                            rgbs[i] = new RGB(colors[colorIndex + 2] & 0xFF, colors[colorIndex + 1] & 0xFF, colors[colorIndex] & 0xFF);
+                            colorIndex += 4;
+                        }
+                    }
+                } else {
+                    int srcIndex = BITMAPINFOHEADER.sizeof;
+                    for (int i = 0; i < numColors; i++) {
+                        rgbs[i] = new RGB(bmi[srcIndex + 2] & 0xFF, bmi[srcIndex + 1] & 0xFF, bmi[srcIndex] & 0xFF);
+                        srcIndex += 4;
+                    }
+                }
+                palette = new PaletteData(rgbs);
+            } else if (depth is 16) {
+                palette = new PaletteData(0x7C00, 0x3E0, 0x1F);
+            } else if (depth is 24) {
+                palette = new PaletteData(0xFF, 0xFF00, 0xFF0000);
+            } else if (depth is 32) {
+                palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
+            } else {
+                DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+            }
+            /* Clean up */
+            OS.SelectObject(hBitmapDC, hOldBitmap);
+            if (oldPalette !is 0) {
+                OS.SelectPalette(hBitmapDC, oldPalette, false);
+                OS.RealizePalette(hBitmapDC);
+            }
+            if (OS.IsWinCE) {
+                if (handle !is this.handle) {
+                    /* free temporary DIB */
+                    OS.DeleteObject (handle);
+                }
+            }
+            OS.DeleteDC(hBitmapDC);
+
+            /* Release the HDC for the device */
+            device.internal_dispose_GC(hDC, null);
+
+            /* Construct and return the ImageData */
+            ImageData imageData = new ImageData(width, height, depth, palette, 4, data);
+            imageData.transparentPixel = this.transparentPixel;
+            imageData.alpha = alpha;
+            if (alpha is -1 && alphaData !is null) {
+                imageData.alphaData = new byte[alphaData.length];
+                System.arraycopy(alphaData, 0, imageData.alphaData, 0, alphaData.length);
+            }
+            return imageData;
+        }
+        default:
+            DWT.error(DWT.ERROR_INVALID_IMAGE);
+            return null;
+    }
+}
+
+/**
+ * Returns an integer hash code for the receiver. Any two
+ * objects that return <code>true</code> when passed to
+ * <code>equals</code> must return the same value for this
+ * method.
+ *
+ * @return the receiver's hash
+ *
+ * @see #equals
+ */
+public hash_t toHash () {
+    return cast(hash_t)handle;
+}
+
+void init(Device device, int width, int height) {
+    if (width <= 0 || height <= 0) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    this.device = device;
+    type = DWT.BITMAP;
+    auto hDC = device.internal_new_GC(null);
+    handle = OS.CreateCompatibleBitmap(hDC, width, height);
+    /*
+    * Feature in Windows.  CreateCompatibleBitmap() may fail
+    * for large images.  The fix is to create a DIB section
+    * in that case.
+    */
+    if (handle is null) {
+        int bits = OS.GetDeviceCaps(hDC, OS.BITSPIXEL);
+        int planes = OS.GetDeviceCaps(hDC, OS.PLANES);
+        int depth = bits * planes;
+        if (depth < 16) depth = 16;
+        handle = createDIB(width, height, depth);
+    }
+    if (handle !is null) {
+        auto memDC = OS.CreateCompatibleDC(hDC);
+        auto hOldBitmap = OS.SelectObject(memDC, handle);
+        OS.PatBlt(memDC, 0, 0, width, height, OS.PATCOPY);
+        OS.SelectObject(memDC, hOldBitmap);
+        OS.DeleteDC(memDC);
+    }
+    device.internal_dispose_GC(hDC, null);
+    if (handle is null) {
+        DWT.error(DWT.ERROR_NO_HANDLES, null, device.getLastError());
+    }
+}
+
+static HGDIOBJ createDIB(int width, int height, int depth) {
+    BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
+    bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+    bmiHeader.biWidth = width;
+    bmiHeader.biHeight = -height;
+    bmiHeader.biPlanes = 1;
+    bmiHeader.biBitCount = cast(short)depth;
+    if (OS.IsWinCE) bmiHeader.biCompression = OS.BI_BITFIELDS;
+    else bmiHeader.biCompression = OS.BI_RGB;
+    byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + (OS.IsWinCE ? 12 : 0)];
+    OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+    /* Set the rgb colors into the bitmap info */
+    if (OS.IsWinCE) {
+        int redMask = 0xFF00;
+        int greenMask = 0xFF0000;
+        int blueMask = 0xFF000000;
+        /* big endian */
+        int offset = BITMAPINFOHEADER.sizeof;
+        bmi[offset] = cast(byte)((redMask & 0xFF000000) >> 24);
+        bmi[offset + 1] = cast(byte)((redMask & 0xFF0000) >> 16);
+        bmi[offset + 2] = cast(byte)((redMask & 0xFF00) >> 8);
+        bmi[offset + 3] = cast(byte)((redMask & 0xFF) >> 0);
+        bmi[offset + 4] = cast(byte)((greenMask & 0xFF000000) >> 24);
+        bmi[offset + 5] = cast(byte)((greenMask & 0xFF0000) >> 16);
+        bmi[offset + 6] = cast(byte)((greenMask & 0xFF00) >> 8);
+        bmi[offset + 7] = cast(byte)((greenMask & 0xFF) >> 0);
+        bmi[offset + 8] = cast(byte)((blueMask & 0xFF000000) >> 24);
+        bmi[offset + 9] = cast(byte)((blueMask & 0xFF0000) >> 16);
+        bmi[offset + 10] = cast(byte)((blueMask & 0xFF00) >> 8);
+        bmi[offset + 11] = cast(byte)((blueMask & 0xFF) >> 0);
+    }
+
+    int[] pBits = new int[1];
+    return OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
+}
+
+/**
+ * Feature in WinCE.  GetIconInfo is not available in WinCE.
+ * The workaround is to cache the object ImageData for images
+ * of type DWT.ICON. The bitmaps hbmMask and hbmColor can then
+ * be reconstructed by using our version of getIconInfo.
+ * This function takes an ICONINFO object and sets the fields
+ * hbmMask and hbmColor with the corresponding bitmaps it has
+ * created.
+ * Note.  These bitmaps must be freed - as they would have to be
+ * if the regular GetIconInfo had been used.
+ */
+static void GetIconInfo(Image image, ICONINFO* info) {
+    int[] result = init(image.device, null, image.data);
+    info.hbmColor = result[0];
+    info.hbmMask = result[1];
+}
+
+static int[] init(Device device, Image image, ImageData i) {
+    if (image !is null) image.device = device;
+
+    /*
+     * BUG in Windows 98:
+     * A monochrome DIBSection will display as solid black
+     * on Windows 98 machines, even though it contains the
+     * correct data. The fix is to convert 1-bit ImageData
+     * into 4-bit ImageData before creating the image.
+     */
+    /* Windows does not support 2-bit images. Convert to 4-bit image. */
+    if ((OS.IsWin95 && i.depth is 1 && i.getTransparencyType() !is DWT.TRANSPARENCY_MASK) || i.depth is 2) {
+        ImageData img = new ImageData(i.width, i.height, 4, i.palette);
+        ImageData.blit(ImageData.BLIT_SRC,
+            i.data, i.depth, i.bytesPerLine, i.getByteOrder(), 0, 0, i.width, i.height, null, null, null,
+            ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
+            img.data, img.depth, img.bytesPerLine, i.getByteOrder(), 0, 0, img.width, img.height, null, null, null,
+            false, false);
+        img.transparentPixel = i.transparentPixel;
+        img.maskPad = i.maskPad;
+        img.maskData = i.maskData;
+        img.alpha = i.alpha;
+        img.alphaData = i.alphaData;
+        i = img;
+    }
+    /*
+     * Windows supports 16-bit mask of (0x7C00, 0x3E0, 0x1F),
+     * 24-bit mask of (0xFF0000, 0xFF00, 0xFF) and 32-bit mask
+     * (0x00FF0000, 0x0000FF00, 0x000000FF) as documented in
+     * MSDN BITMAPINFOHEADER.  Make sure the image is
+     * Windows-supported.
+     */
+    /*
+    * Note on WinCE.  CreateDIBSection requires the biCompression
+    * field of the BITMAPINFOHEADER to be set to BI_BITFIELDS for
+    * 16 and 32 bit direct images (see MSDN for CreateDIBSection).
+    * In this case, the color mask can be set to any value.  For
+    * consistency, it is set to the same mask used by non WinCE
+    * platforms in BI_RGB mode.
+    */
+    if (i.palette.isDirect) {
+        final PaletteData palette = i.palette;
+        final int redMask = palette.redMask;
+        final int greenMask = palette.greenMask;
+        final int blueMask = palette.blueMask;
+        int newDepth = i.depth;
+        int newOrder = ImageData.MSB_FIRST;
+        PaletteData newPalette = null;
+
+        switch (i.depth) {
+            case 8:
+                newDepth = 16;
+                newOrder = ImageData.LSB_FIRST;
+                newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F);
+                break;
+            case 16:
+                newOrder = ImageData.LSB_FIRST;
+                if (!(redMask is 0x7C00 && greenMask is 0x3E0 && blueMask is 0x1F)) {
+                    newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F);
+                }
+                break;
+            case 24:
+                if (!(redMask is 0xFF && greenMask is 0xFF00 && blueMask is 0xFF0000)) {
+                    newPalette = new PaletteData(0xFF, 0xFF00, 0xFF0000);
+                }
+                break;
+            case 32:
+                if (!(redMask is 0xFF00 && greenMask is 0xFF0000 && blueMask is 0xFF000000)) {
+                    newPalette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
+                }
+                break;
+            default:
+                DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+        }
+        if (newPalette !is null) {
+            ImageData img = new ImageData(i.width, i.height, newDepth, newPalette);
+            ImageData.blit(ImageData.BLIT_SRC,
+                    i.data, i.depth, i.bytesPerLine, i.getByteOrder(), 0, 0, i.width, i.height, redMask, greenMask, blueMask,
+                    ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
+                    img.data, img.depth, img.bytesPerLine, newOrder, 0, 0, img.width, img.height, newPalette.redMask, newPalette.greenMask, newPalette.blueMask,
+                    false, false);
+            if (i.transparentPixel !is -1) {
+                img.transparentPixel = newPalette.getPixel(palette.getRGB(i.transparentPixel));
+            }
+            img.maskPad = i.maskPad;
+            img.maskData = i.maskData;
+            img.alpha = i.alpha;
+            img.alphaData = i.alphaData;
+            i = img;
+        }
+    }
+    /* Construct bitmap info header by hand */
+    RGB[] rgbs = i.palette.getRGBs();
+    bool useBitfields = OS.IsWinCE && (i.depth is 16 || i.depth is 32);
+    BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER();
+    bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+    bmiHeader.biWidth = i.width;
+    bmiHeader.biHeight = -i.height;
+    bmiHeader.biPlanes = 1;
+    bmiHeader.biBitCount = cast(short)i.depth;
+    if (useBitfields) bmiHeader.biCompression = OS.BI_BITFIELDS;
+    else bmiHeader.biCompression = OS.BI_RGB;
+    bmiHeader.biClrUsed = rgbs is null ? 0 : rgbs.length;
+    byte[] bmi;
+    if (i.palette.isDirect)
+        bmi = new byte[BITMAPINFOHEADER.sizeof + (useBitfields ? 12 : 0)];
+    else
+        bmi = new byte[BITMAPINFOHEADER.sizeof + rgbs.length * 4];
+    OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+    /* Set the rgb colors into the bitmap info */
+    int offset = BITMAPINFOHEADER.sizeof;
+    if (i.palette.isDirect) {
+        if (useBitfields) {
+            PaletteData palette = i.palette;
+            int redMask = palette.redMask;
+            int greenMask = palette.greenMask;
+            int blueMask = palette.blueMask;
+            /*
+             * The color masks must be written based on the
+             * endianness of the ImageData.
+             */
+            if (i.getByteOrder() is ImageData.LSB_FIRST) {
+                bmi[offset] = cast(byte)((redMask & 0xFF) >> 0);
+                bmi[offset + 1] = cast(byte)((redMask & 0xFF00) >> 8);
+                bmi[offset + 2] = cast(byte)((redMask & 0xFF0000) >> 16);
+                bmi[offset + 3] = cast(byte)((redMask & 0xFF000000) >> 24);
+                bmi[offset + 4] = cast(byte)((greenMask & 0xFF) >> 0);
+                bmi[offset + 5] = cast(byte)((greenMask & 0xFF00) >> 8);
+                bmi[offset + 6] = cast(byte)((greenMask & 0xFF0000) >> 16);
+                bmi[offset + 7] = cast(byte)((greenMask & 0xFF000000) >> 24);
+                bmi[offset + 8] = cast(byte)((blueMask & 0xFF) >> 0);
+                bmi[offset + 9] = cast(byte)((blueMask & 0xFF00) >> 8);
+                bmi[offset + 10] = cast(byte)((blueMask & 0xFF0000) >> 16);
+                bmi[offset + 11] = cast(byte)((blueMask & 0xFF000000) >> 24);
+            } else {
+                bmi[offset] = cast(byte)((redMask & 0xFF000000) >> 24);
+                bmi[offset + 1] = cast(byte)((redMask & 0xFF0000) >> 16);
+                bmi[offset + 2] = cast(byte)((redMask & 0xFF00) >> 8);
+                bmi[offset + 3] = cast(byte)((redMask & 0xFF) >> 0);
+                bmi[offset + 4] = cast(byte)((greenMask & 0xFF000000) >> 24);
+                bmi[offset + 5] = cast(byte)((greenMask & 0xFF0000) >> 16);
+                bmi[offset + 6] = cast(byte)((greenMask & 0xFF00) >> 8);
+                bmi[offset + 7] = cast(byte)((greenMask & 0xFF) >> 0);
+                bmi[offset + 8] = cast(byte)((blueMask & 0xFF000000) >> 24);
+                bmi[offset + 9] = cast(byte)((blueMask & 0xFF0000) >> 16);
+                bmi[offset + 10] = cast(byte)((blueMask & 0xFF00) >> 8);
+                bmi[offset + 11] = cast(byte)((blueMask & 0xFF) >> 0);
+            }
+        }
+    } else {
+        for (int j = 0; j < rgbs.length; j++) {
+            bmi[offset] = cast(byte)rgbs[j].blue;
+            bmi[offset + 1] = cast(byte)rgbs[j].green;
+            bmi[offset + 2] = cast(byte)rgbs[j].red;
+            bmi[offset + 3] = 0;
+            offset += 4;
+        }
+    }
+    int[] pBits = new int[1];
+    int hDib = OS.CreateDIBSection(0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
+    if (hDib is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    /* In case of a scanline pad other than 4, do the work to convert it */
+    byte[] data = i.data;
+    if (i.scanlinePad !is 4 && (i.bytesPerLine % 4 !is 0)) {
+        data = ImageData.convertPad(data, i.width, i.height, i.depth, i.scanlinePad, 4);
+    }
+    OS.MoveMemory(pBits[0], data, data.length);
+
+    int[] result = null;
+    if (i.getTransparencyType() is DWT.TRANSPARENCY_MASK) {
+        /* Get the HDC for the device */
+        auto hDC = device.internal_new_GC(null);
+
+        /* Create the color bitmap */
+        auto hdcSrc = OS.CreateCompatibleDC(hDC);
+        OS.SelectObject(hdcSrc, hDib);
+        int hBitmap = OS.CreateCompatibleBitmap(hDC, i.width, i.height);
+        if (hBitmap is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        auto hdcDest = OS.CreateCompatibleDC(hDC);
+        OS.SelectObject(hdcDest, hBitmap);
+        OS.BitBlt(hdcDest, 0, 0, i.width, i.height, hdcSrc, 0, 0, OS.SRCCOPY);
+
+        /* Release the HDC for the device */
+        device.internal_dispose_GC(hDC, null);
+
+        /* Create the mask. Windows requires icon masks to have a scanline pad of 2. */
+        byte[] maskData = ImageData.convertPad(i.maskData, i.width, i.height, 1, i.maskPad, 2);
+        int hMask = OS.CreateBitmap(i.width, i.height, 1, 1, maskData);
+        if (hMask is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+        OS.SelectObject(hdcSrc, hMask);
+        OS.PatBlt(hdcSrc, 0, 0, i.width, i.height, OS.DSTINVERT);
+        OS.DeleteDC(hdcSrc);
+        OS.DeleteDC(hdcDest);
+        OS.DeleteObject(hDib);
+
+        if (image is null) {
+            result = [hBitmap, hMask];
+        } else {
+            /* Create the icon */
+            ICONINFO info = new ICONINFO();
+            info.fIcon = true;
+            info.hbmColor = hBitmap;
+            info.hbmMask = hMask;
+            int hIcon = OS.CreateIconIndirect(info);
+            if (hIcon is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+            OS.DeleteObject(hBitmap);
+            OS.DeleteObject(hMask);
+            image.handle = hIcon;
+            image.type = DWT.ICON;
+            if (OS.IsWinCE) image.data = i;
+        }
+    } else {
+        if (image is null) {
+            result = [hDib];
+        } else {
+            image.handle = hDib;
+            image.type = DWT.BITMAP;
+            image.transparentPixel = i.transparentPixel;
+            if (image.transparentPixel is -1) {
+                image.alpha = i.alpha;
+                if (i.alpha is -1 && i.alphaData !is null) {
+                    int length = i.alphaData.length;
+                    image.alphaData = new byte[length];
+                    System.arraycopy(i.alphaData, 0, image.alphaData, 0, length);
+                }
+            }
+        }
+    }
+    return result;
+}
+
+static int[] init(Device device, Image image, ImageData source, ImageData mask) {
+    /* Create a temporary image and locate the black pixel */
+    ImageData imageData;
+    int blackIndex = 0;
+    if (source.palette.isDirect) {
+        imageData = new ImageData(source.width, source.height, source.depth, source.palette);
+    } else {
+        RGB black = new RGB(0, 0, 0);
+        RGB[] rgbs = source.getRGBs();
+        if (source.transparentPixel !is -1) {
+            /*
+             * The source had transparency, so we can use the transparent pixel
+             * for black.
+             */
+            RGB[] newRGBs = new RGB[rgbs.length];
+            System.arraycopy(rgbs, 0, newRGBs, 0, rgbs.length);
+            if (source.transparentPixel >= newRGBs.length) {
+                /* Grow the palette with black */
+                rgbs = new RGB[source.transparentPixel + 1];
+                System.arraycopy(newRGBs, 0, rgbs, 0, newRGBs.length);
+                for (int i = newRGBs.length; i <= source.transparentPixel; i++) {
+                    rgbs[i] = new RGB(0, 0, 0);
+                }
+            } else {
+                newRGBs[source.transparentPixel] = black;
+                rgbs = newRGBs;
+            }
+            blackIndex = source.transparentPixel;
+            imageData = new ImageData(source.width, source.height, source.depth, new PaletteData(rgbs));
+        } else {
+            while (blackIndex < rgbs.length) {
+                if (rgbs[blackIndex].equals(black)) break;
+                blackIndex++;
+            }
+            if (blackIndex is rgbs.length) {
+                /*
+                 * We didn't find black in the palette, and there is no transparent
+                 * pixel we can use.
+                 */
+                if ((1 << source.depth) > rgbs.length) {
+                    /* We can grow the palette and add black */
+                    RGB[] newRGBs = new RGB[rgbs.length + 1];
+                    System.arraycopy(rgbs, 0, newRGBs, 0, rgbs.length);
+                    newRGBs[rgbs.length] = black;
+                    rgbs = newRGBs;
+                } else {
+                    /* No room to grow the palette */
+                    blackIndex = -1;
+                }
+            }
+            imageData = new ImageData(source.width, source.height, source.depth, new PaletteData(rgbs));
+        }
+    }
+    if (blackIndex is -1) {
+        /* There was no black in the palette, so just copy the data over */
+        System.arraycopy(source.data, 0, imageData.data, 0, imageData.data.length);
+    } else {
+        /* Modify the source image to contain black wherever the mask is 0 */
+        int[] imagePixels = new int[imageData.width];
+        int[] maskPixels = new int[mask.width];
+        for (int y = 0; y < imageData.height; y++) {
+            source.getPixels(0, y, imageData.width, imagePixels, 0);
+            mask.getPixels(0, y, mask.width, maskPixels, 0);
+            for (int i = 0; i < imagePixels.length; i++) {
+                if (maskPixels[i] is 0) imagePixels[i] = blackIndex;
+            }
+            imageData.setPixels(0, y, source.width, imagePixels, 0);
+        }
+    }
+    imageData.maskPad = mask.scanlinePad;
+    imageData.maskData = mask.data;
+    return init(device, image, imageData);
+}
+void init(Device device, ImageData i) {
+    if (i is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    init(device, this, i);
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new GC handle.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Image</code>. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ *
+ * @param data the platform specific GC data
+ * @return the platform specific GC handle
+ */
+public HDC internal_new_GC (GCData data) {
+    if (handle is null) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    /*
+    * Create a new GC that can draw into the image.
+    * Only supported for bitmaps.
+    */
+    if (type !is DWT.BITMAP || memGC !is null) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+
+    /* Create a compatible HDC for the device */
+    auto hDC = device.internal_new_GC(null);
+    auto imageDC = OS.CreateCompatibleDC(hDC);
+    device.internal_dispose_GC(hDC, null);
+    if (imageDC is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+
+    if (data !is null) {
+        /* Set the GCData fields */
+        int mask = DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
+        if ((data.style & mask) !is 0) {
+            data.layout = (data.style & DWT.RIGHT_TO_LEFT) !is 0 ? OS.LAYOUT_RTL : 0;
+        } else {
+            data.style |= DWT.LEFT_TO_RIGHT;
+        }
+        data.device = device;
+        data.image = this;
+        data.hFont = device.systemFont;
+    }
+    return imageDC;
+}
+
+/**
+ * Invokes platform specific functionality to dispose a GC handle.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Image</code>. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ *
+ * @param hDC the platform specific GC handle
+ * @param data the platform specific GC data
+ */
+public void internal_dispose_GC (HDC hDC, GCData data) {
+    OS.DeleteDC(hDC);
+}
+
+/**
+ * Returns <code>true</code> if the image has been disposed,
+ * and <code>false</code> otherwise.
+ * <p>
+ * This method gets the dispose state for the image.
+ * When an image has been disposed, it is an error to
+ * invoke any other method using the image.
+ *
+ * @return <code>true</code> when the image is disposed and <code>false</code> otherwise
+ */
+public bool isDisposed() {
+    return handle is null;
+}
+
+/**
+ * Sets the color to which to map the transparent pixel.
+ * <p>
+ * There are certain uses of <code>Images</code> that do not support
+ * transparency (for example, setting an image into a button or label).
+ * In these cases, it may be desired to simulate transparency by using
+ * the background color of the widget to paint the transparent pixels
+ * of the image. This method specifies the color that will be used in
+ * these cases. For example:
+ * <pre>
+ *    Button b = new Button();
+ *    image.setBackground(b.getBackground());
+ *    b.setImage(image);
+ * </pre>
+ * </p><p>
+ * The image may be modified by this operation (in effect, the
+ * transparent regions may be filled with the supplied color).  Hence
+ * this operation is not reversible and it is not legal to call
+ * this function twice or with a null argument.
+ * </p><p>
+ * This method has no effect if the receiver does not have a transparent
+ * pixel value.
+ * </p>
+ *
+ * @param color the color to use when a transparent pixel is specified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the color is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void setBackground(Color color) {
+    /*
+    * Note.  Not implemented on WinCE.
+    */
+    if (OS.IsWinCE) return;
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    if (color is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (transparentPixel is -1) return;
+
+    /* Get the HDC for the device */
+    auto hDC = device.internal_new_GC(null);
+
+    /* Change the background color in the image */
+    BITMAP bm = new BITMAP();
+    OS.GetObject(handle, BITMAP.sizeof, bm);
+    auto hdcMem = OS.CreateCompatibleDC(hDC);
+    OS.SelectObject(hdcMem, handle);
+    int maxColors = 1 << bm.bmBitsPixel;
+    byte[] colors = new byte[maxColors * 4];
+    if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+    int numColors = OS.GetDIBColorTable(hdcMem, 0, maxColors, colors);
+    int offset = transparentPixel * 4;
+    colors[offset] = cast(byte)color.getBlue();
+    colors[offset + 1] = cast(byte)color.getGreen();
+    colors[offset + 2] = cast(byte)color.getRed();
+    if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+    OS.SetDIBColorTable(hdcMem, 0, numColors, colors);
+    OS.DeleteDC(hdcMem);
+
+    /* Release the HDC for the device */
+    device.internal_dispose_GC(hDC, null);
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the receiver
+ */
+public char[] toString () {
+    if (isDisposed()) return "Image {*DISPOSED*}";
+    return Format( "Image {{{}}", handle );
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new image.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Image</code>. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ *
+ * @param device the device on which to allocate the color
+ * @param type the type of the image (<code>DWT.BITMAP</code> or <code>DWT.ICON</code>)
+ * @param handle the OS handle for the image
+ * @return a new image object containing the specified device, type and handle
+ */
+public static Image win32_new(Device device, int type, HGDIOBJ handle) {
+    if (device is null) device = Device.getDevice();
+    Image image = new Image();
+    image.type = type;
+    image.handle = handle;
+    image.device = device;
+    return image;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageData.d	Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,3653 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.graphics.ImageData;
+
+
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.graphics.Image;
+import dwt.graphics.GC;
+import dwt.graphics.Device;
+import dwt.graphics.ImageDataLoader;
+import dwt.DWT;
+import dwt.internal.CloneableCompatibility;
+
+public import dwt.dwthelper.InputStream;
+
+
+/**
+ * Instances of this class are device-independent descriptions
+ * of images. They are typically used as an intermediate format
+ * between loading from or writing to streams and creating an
+ * <code>Image</code>.
+ * <p>
+ * Note that the public fields <code>x</code>, <code>y</code>,
+ * <code>disposalMethod</code> and <code>delayTime</code> are
+ * typically only used when the image is in a set of images used
+ * for animation.
+ * </p>
+ *
+ * @see Image
+ * @see ImageLoader
+ */
+
+public final class ImageData : CloneableCompatibility {
+
+    /**
+     * The width of the image, in pixels.
+     */
+    public int width;
+
+    /**
+     * The height of the image, in pixels.
+     */
+    public int height;
+
+    /**
+     * The color depth of the image, in bits per pixel.
+     * <p>
+     * Note that a depth of 8 or less does not necessarily
+     * mean that the image is palette indexed, or
+     * conversely that a depth greater than 8 means that
+     * the image is direct color.  Check the associated
+     * PaletteData's isDirect field for such determinations.
+     */
+    public int depth;
+
+    /**
+     * The scanline padding.
+     * <p>
+     * If one scanline of the image is not a multiple of
+     * this number, it will be padded with zeros until it is.
+     * </p>
+     */
+    public int scanlinePad;
+
+    /**
+     * The number of bytes per scanline.
+     * <p>
+     * This is a multiple of the scanline padding.
+     * </p>
+     */
+    public int bytesPerLine;
+
+    /**
+     * The pixel data of the image.
+     * <p>
+     * Note that for 16 bit depth images the pixel data is stored
+     * in least significant byte order; however, for 24bit and
+     * 32bit depth images the pixel data is stored in most
+     * significant byte order.
+     * </p>
+     */
+    public byte[] data;
+
+    /**
+     * The color table for the image.
+     */
+    public PaletteData palette;
+
+    /**
+     * The transparent pixel.
+     * <p>
+     * Pixels with this value are transparent.
+     * </p><p>
+     * The default is -1 which means 'no transparent pixel'.
+     * </p>
+     */
+    public int transparentPixel;
+
+    /**
+     * An icon-specific field containing the data from the icon mask.
+     * <p>
+     * This is a 1 bit bitmap stored with the most significant
+     * bit first.  The number of bytes per scanline is
+     * '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'.
+     * </p><p>
+     * The default is null which means 'no transparency mask'.
+     * </p>
+     */
+    public byte[] maskData;
+
+    /**
+     * An icon-specific field containing the scanline pad of the mask.
+     * <p>
+     * If one scanline of the transparency mask is not a
+     * multiple of this number, it will be padded with zeros until
+     * it is.
+     * </p>
+     */
+    public int maskPad;
+
+    /**
+     * The alpha data of the image.
+     * <p>
+     * Every pixel can have an <em>alpha blending</em> value that
+     * varies from 0, meaning fully transparent, to 255 meaning
+     * fully opaque.  The number of bytes per scanline is
+     * 'width'.
+     * </p>
+     */
+    public byte[] alphaData;
+
+    /**
+     * The global alpha value to be used for every pixel.
+     * <p>
+     * If this value is set, the <code>alphaData</code> field
+     * is ignored and when the image is rendered each pixel
+     * will be blended with the background an amount
+     * proportional to this value.
+     * </p><p>
+     * The default is -1 which means 'no global alpha value'
+     * </p>
+     */
+    public int alpha;
+
+    /**
+     * The type of file from which the image was read.
+     *
+     * It is expressed as one of the following values:
+     * <dl>
+     * <dt><code>IMAGE_BMP</code></dt>
+     * <dd>Windows BMP file format, no compression</dd>
+     * <dt><code>IMAGE_BMP_RLE</code></dt>
+     * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+     * <dt><code>IMAGE_GIF</code></dt>
+     * <dd>GIF file format</dd>
+     * <dt><code>IMAGE_ICO</code></dt>
+     * <dd>Windows ICO file format</dd>
+     * <dt><code>IMAGE_JPEG</code></dt>
+     * <dd>JPEG file format</dd>
+     * <dt><code>IMAGE_PNG</code></dt>
+     * <dd>PNG file format</dd>
+     * </dl>
+     */
+    public int type;
+
+    /**
+     * The x coordinate of the top left corner of the image
+     * within the logical screen (this field corresponds to
+     * the GIF89a Image Left Position value).
+     */
+    public int x;
+
+    /**
+     * The y coordinate of the top left corner of the image
+     * within the logical screen (this field corresponds to
+     * the GIF89a Image Top Position value).
+     */
+    public int y;
+
+    /**
+     * A description of how to dispose of the current image
+     * before displaying the next.
+     *
+     * It is expressed as one of the following values:
+     * <dl>
+     * <dt><code>DM_UNSPECIFIED</code></dt>
+     * <dd>disposal method not specified</dd>
+     * <dt><code>DM_FILL_NONE</code></dt>
+     * <dd>do nothing - leave the image in place</dd>
+     * <dt><code>DM_FILL_BACKGROUND</code></dt>
+     * <dd>fill with the background color</dd>
+     * <dt><code>DM_FILL_PREVIOUS</code></dt>
+     * <dd>restore the previous picture</dd>
+     * </dl>
+     * (this field corresponds to the GIF89a Disposal Method value)
+     */
+    public int disposalMethod;
+
+    /**
+     * The time to delay before displaying the next image
+     * in an animation (this field corresponds to the GIF89a
+     * Delay Time value).
+     */
+    public int delayTime;
+
+    /**
+     * Arbitrary channel width data to 8-bit conversion table.
+     */
+    private static byte[][] ANY_TO_EIGHT;
+    private static byte[] ONE_TO_ONE_MAPPING;
+
+    private static bool static_this_completed = false;
+    private static void static_this() {
+        if( static_this_completed ) return;
+        synchronized {
+            if( static_this_completed ) return;
+            ANY_TO_EIGHT = new byte[][](9);
+            for (int b = 0; b < 9; ++b) {
+                byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b];
+                if (b is 0) continue;
+                int inc = 0;
+                for (int bit = 0x10000; (bit >>= b) !is 0;) inc |= bit;
+                for (int v = 0, p = 0; v < 0x10000; v+= inc) data[p++] = cast(byte)(v >> 8);
+            }
+            ONE_TO_ONE_MAPPING = ANY_TO_EIGHT[8];
+            static_this_completed = true;
+        }
+    }
+
+    /**
+     * Scaled 8x8 Bayer dither matrix.
+     */
+    static const int[][] DITHER_MATRIX = [
+        [ 0xfc0000, 0x7c0000, 0xdc0000, 0x5c0000, 0xf40000, 0x740000, 0xd40000, 0x540000 ],
+        [ 0x3c0000, 0xbc0000, 0x1c0000, 0x9c0000, 0x340000, 0xb40000, 0x140000, 0x940000 ],
+        [ 0xcc0000, 0x4c0000, 0xec0000, 0x6c0000, 0xc40000, 0x440000, 0xe40000, 0x640000 ],
+        [ 0x0c0000, 0x8c0000, 0x2c0000, 0xac0000, 0x040000, 0x840000, 0x240000, 0xa40000 ],
+        [ 0xf00000, 0x700000, 0xd00000, 0x500000, 0xf80000, 0x780000, 0xd80000, 0x580000 ],
+        [ 0x300000, 0xb00000, 0x100000, 0x900000, 0x380000, 0xb80000, 0x180000, 0x980000 ],
+        [ 0xc00000, 0x400000, 0xe00000, 0x600000, 0xc80000, 0x480000, 0xe80000, 0x680000 ],
+        [ 0x000000, 0x800000, 0x200000, 0xa00000, 0x080000, 0x880000, 0x280000, 0xa80000 ]
+    ];
+
+/**
+ * Constructs a new, empty ImageData with the given width, height,
+ * depth and palette. The data will be initialized to an (all zero)
+ * array of the appropriate size.
+ *
+ * @param width the width of the image
+ * @param height the height of the image
+ * @param depth the depth of the image
+ * @param palette the palette of the image (must not be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
+ *          one of 1, 2, 4, 8, 16, 24 or 32</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the palette is null</li>
+ * </ul>
+ */
+public this(int width, int height, int depth, PaletteData palette) {
+    this(width, height, depth, palette,
+        4, null, 0, null,
+        null, -1, -1, DWT.IMAGE_UNDEFINED,
+        0, 0, 0, 0);
+}
+
+/**
+ * Constructs a new, empty ImageData with the given width, height,
+ * depth, palette, scanlinePad and data.
+ *
+ * @param width the width of the image
+ * @param height the height of the image
+ * @param depth the depth of the image
+ * @param palette the palette of the image
+ * @param scanlinePad the padding of each line, in bytes
+ * @param data the data of the image
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
+ *          one of 1, 2, 4, 8, 16, 24 or 32</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the palette or data is null</li>
+ *    <li>ERROR_CANNOT_BE_ZERO - if the scanlinePad is zero</li>
+ * </ul>
+ */
+public this(int width, int height, int depth, PaletteData palette, int scanlinePad, byte[] data) {
+    this(width, height, depth, palette,
+        scanlinePad, checkData(data), 0, null,
+        null, -1, -1, DWT.IMAGE_UNDEFINED,
+        0, 0, 0, 0);
+}
+
+/**
+ * Constructs an <code>ImageData</code> loaded from the specified
+ * input stream. Throws an error if an error occurs while loading
+ * the image, or if the image has an unsupported type.  Application
+ * code is still responsible for closing the input stream.
+ * <p>
+ * This constructor is provided for convenience when loading a single
+ * image only. If the stream contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * <code>ImageLoader.load()</code>.
+ * </p><p>
+ * This constructor may be used to load a resource as follows:
+ * </p>
+ * <pre>
+ *     static ImageData loadImageData (Class clazz, String string) {
+ *          InputStream stream = clazz.getResourceAsStream (string);
+ *          if (stream is null) return null;
+ *          ImageData imageData = null;
+ *          try {
+ *               imageData = new ImageData (stream);
+ *          } catch (DWTException ex) {
+ *          } finally {
+ *               try {
+ *                    stream.close ();
+ *               } catch (IOException ex) {}
+ *          }
+ *          return imageData;
+ *     }
+ * </pre>
+ *
+ * @param stream the input stream to load the image from (must not be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ *
+ * @see ImageLoader#load(InputStream)
+ */
+public this(InputStream stream) {
+    ImageData[] data = ImageDataLoader.load(stream);
+    if (data.length < 1) DWT.error(DWT.ERROR_INVALID_IMAGE);
+    ImageData i = data[0];
+    setAllFields(
+        i.width,
+        i.height,
+        i.depth,
+        i.scanlinePad,
+        i.bytesPerLine,
+        i.data,
+        i.palette,
+        i.transparentPixel,
+        i.maskData,
+        i.maskPad,
+        i.alphaData,
+        i.alpha,
+        i.type,
+        i.x,
+        i.y,
+        i.disposalMethod,
+        i.delayTime);
+}
+
+/**
+ * Constructs an <code>ImageData</code> loaded from a file with the
+ * specified name. Throws an error if an error occurs loading the
+ * image, or if the image has an unsupported type.
+ * <p>
+ * This constructor is provided for convenience when loading a single
+ * image only. If the file contains multiple images, only the first
+ * one will be loaded. To load multiple images, use
+ * <code>ImageLoader.load()</code>.
+ * </p>
+ *
+ * @param filename the name of the file to load the image from (must not be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the file</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+ * </ul>
+ */
+public this(char[] filename) {
+    ImageData[] data = ImageDataLoader.load(filename);
+    if (data.length < 1) DWT.error(DWT.ERROR_INVALID_IMAGE);
+    ImageData i = data[0];
+    setAllFields(
+        i.width,
+        i.height,
+        i.depth,
+        i.scanlinePad,
+        i.bytesPerLine,
+        i.data,
+        i.palette,
+        i.transparentPixel,
+        i.maskData,
+        i.maskPad,
+        i.alphaData,
+        i.alpha,
+        i.type,
+        i.x,
+        i.y,
+        i.disposalMethod,
+        i.delayTime);
+}
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+private this() {
+}
+
+/**
+ * Constructs an image data by giving values for all non-computable fields.
+ * <p>
+ * This method is for internal use, and is not described further.
+ * </p>
+ */
+this(
+    int width, int height, int depth, PaletteData palette,
+    int scanlinePad, byte[] data, int maskPad, byte[] maskData,
+    byte[] alphaData, int alpha, int transparentPixel, int type,
+    int x, int y, int disposalMethod, int delayTime)
+{
+    if (palette is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (!(depth is 1 || depth is 2 || depth is 4 || depth is 8
+        || depth is 16 || depth is 24 || depth is 32)) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    if (width <= 0 || height <= 0) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    if (scanlinePad is 0) DWT.error (DWT.ERROR_CANNOT_BE_ZERO);
+
+    int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1))
+        / scanlinePad * scanlinePad;
+    setAllFields(
+        width,
+        height,
+        depth,
+        scanlinePad,
+        bytesPerLine,
+        data !is null ? data : new byte[bytesPerLine * height],
+        palette,
+        transparentPixel,
+        maskData,
+        maskPad,
+        alphaData,
+        alpha,
+        type,
+        x,
+        y,
+        disposalMethod,
+        delayTime);
+}
+
+/**
+ * Initializes all fields in the receiver. This method must be called
+ * by all public constructors to ensure that all fields are initialized
+ * for a new ImageData object. If a new field is added to the class,
+ * then it must be added to this method.
+ * <p>
+ * This method is for internal use, and is not described further.
+ * </p>
+ */
+void setAllFields(int width, int height, int depth, int scanlinePad,
+    int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel,
+    byte[] maskData, int maskPad, byte[] alphaData, int alpha,
+    int type, int x, int y, int disposalMethod, int delayTime) {
+
+    this.width = width;
+    this.height = height;
+    this.depth = depth;
+    this.scanlinePad = scanlinePad;
+    this.bytesPerLine = bytesPerLine;
+    this.data = data;
+    this.palette = palette;
+    this.transparentPixel = transparentPixel;
+    this.maskData = maskData;
+    this.maskPad = maskPad;
+    this.alphaData = alphaData;
+    this.alpha = alpha;
+    this.type = type;
+    this.x = x;
+    this.y = y;
+    this.disposalMethod = disposalMethod;
+    this.delayTime = delayTime;
+}
+
+/**
+ * Invokes internal DWT functionality to create a new instance of
+ * this class.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>ImageData</code>. It is marked public only so that it
+ * can be shared within the packages provided by DWT. It is subject
+ * to change without notice, and should never be called from
+ * application code.
+ * </p>
+ * <p>
+ * This method is for internal use, and is not described further.
+ * </p>
+ */
+public static ImageData internal_new(
+    int width, int height, int depth, PaletteData palette,
+    int scanlinePad, byte[] data, int maskPad, byte[] maskData,
+    byte[] alphaData, int alpha, int transparentPixel, int type,
+    int x, int y, int disposalMethod, int delayTime)
+{
+    return new ImageData(
+        width, height, depth, palette, scanlinePad, data, maskPad, maskData,
+        alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime);
+}
+
+ImageData colorMaskImage(int pixel) {
+    ImageData mask = new ImageData(width, height, 1, bwPalette(),
+        2, null, 0, null, null, -1, -1, DWT.IMAGE_UNDEFINED,
+        0, 0, 0, 0);
+    int[] row = new int[width];
+    for (int y = 0; y < height; y++) {
+        getPixels(0, y, width, row, 0);
+        for (int i = 0; i < width; i++) {
+            if (pixel !is -1 && row[i] is pixel) {
+                row[i] = 0;
+            } else {
+                row[i] = 1;
+            }
+        }
+        mask.setPixels(0, y, width, row, 0);
+    }
+    return mask;
+}
+
+static byte[] checkData(byte [] data) {
+    if (data is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    return data;
+}
+
+/**
+ * Returns a new instance of the same class as the receiver,
+ * whose slots have been filled in with <em>copies</em> of
+ * the values in the slots of the receiver. That is, the
+ * returned object is a <em>deep copy</em> of the receiver.
+ *
+ * @return a copy of the receiver.
+ */
+public Object clone() {
+    byte[] cloneData = new byte[data.length];
+    System.arraycopy(data, 0, cloneData, 0, data.length);
+    byte[] cloneMaskData = null;
+    if (maskData !is null) {
+        cloneMaskData = new byte[maskData.length];
+        System.arraycopy(maskData, 0, cloneMaskData, 0, maskData.length);
+    }
+    byte[] cloneAlphaData = null;
+    if (alphaData !is null) {
+        cloneAlphaData = new byte[alphaData.length];
+        System.arraycopy(alphaData, 0, cloneAlphaData, 0, alphaData.length);
+    }
+    return new ImageData(
+        width,
+        height,
+        depth,
+        palette,
+        scanlinePad,
+        cloneData,
+        maskPad,
+        cloneMaskData,
+        cloneAlphaData,
+        alpha,
+        transparentPixel,
+        type,
+        x,
+        y,
+        disposalMethod,
+        delayTime);
+}
+
+/**
+ * Returns the alpha value at offset <code>x</code> in
+ * scanline <code>y</code> in the receiver's alpha data.
+ *
+ * @param x the x coordinate of the pixel to get the alpha value of
+ * @param y the y coordinate of the pixel to get the alpha value of
+ * @return the alpha value at the given coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if either argument is out of range</li>
+ * </ul>
+ */
+public int getAlpha(int x, int y) {
+    if (x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+
+    if (alphaData is null) return 255;
+    return alphaData[y * width + x] & 0xFF;
+}
+
+/**
+ * Returns <code>getWidth</code> alpha values starting at offset
+ * <code>x</code> in scanline <code>y</code> in the receiver's alpha
+ * data starting at <code>startIndex</code>.
+ *
+ * @param x the x position of the pixel to begin getting alpha values
+ * @param y the y position of the pixel to begin getting alpha values
+ * @param getWidth the width of the data to get
+ * @param alphas the buffer in which to put the alpha values
+ * @param startIndex the offset into the image to begin getting alpha values
+ *
+ * @exception IndexOutOfBoundsException if getWidth is too large
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
+ * </ul>
+ */
+public void getAlphas(int x, int y, int getWidth, byte[] alphas, int startIndex) {
+    if (alphas is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (getWidth is 0) return;
+
+    if (alphaData is null) {
+        int endIndex = startIndex + getWidth;
+        for (int i = startIndex; i < endIndex; i++) {
+            alphas[i] = cast(byte)255;
+        }
+        return;
+    }
+    // may throw an IndexOutOfBoundsException
+    System.arraycopy(alphaData, y * width + x, alphas, startIndex, getWidth);
+}
+
+/**
+ * Returns the pixel value at offset <code>x</code> in
+ * scanline <code>y</code> in the receiver's data.
+ *
+ * @param x the x position of the pixel to get
+ * @param y the y position of the pixel to get
+ * @return the pixel at the given coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if either argument is out of bounds</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+ * </ul>
+ */
+public int getPixel(int x, int y) {
+    if (x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    int index;
+    int theByte;
+    int mask;
+    switch (depth) {
+        case 32:
+            index = (y * bytesPerLine) + (x * 4);
+            return ((data[index] & 0xFF) << 24) + ((data[index+1] & 0xFF) << 16) +
+                    ((data[index+2] & 0xFF) << 8) + (data[index+3] & 0xFF);
+        case 24:
+            index = (y * bytesPerLine) + (x * 3);
+            return ((data[index] & 0xFF) << 16) + ((data[index+1] & 0xFF) << 8) +
+                (data[index+2] & 0xFF);
+        case 16:
+            index = (y * bytesPerLine) + (x * 2);
+            return ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
+        case 8:
+            index = (y * bytesPerLine) + x ;
+            return data[index] & 0xFF;
+        case 4:
+            index = (y * bytesPerLine) + (x >> 1);
+            theByte = data[index] & 0xFF;
+            if ((x & 0x1) is 0) {
+                return theByte >> 4;
+            } else {
+                return theByte & 0x0F;
+            }
+        case 2:
+            index = (y * bytesPerLine) + (x >> 2);
+            theByte = data[index] & 0xFF;
+            int offset = 3 - (x % 4);
+            mask = 3 << (offset * 2);
+            return (theByte & mask) >> (offset * 2);
+        case 1:
+            index = (y * bytesPerLine) + (x >> 3);
+            theByte = data[index] & 0xFF;
+            mask = 1 << (7 - (x & 0x7));
+            if ((theByte & mask) is 0) {
+                return 0;
+            } else {
+                return 1;
+            }
+        default:
+    }
+    DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+    return 0;
+}
+
+/**
+ * Returns <code>getWidth</code> pixel values starting at offset
+ * <code>x</code> in scanline <code>y</code> in the receiver's
+ * data starting at <code>startIndex</code>.
+ *
+ * @param x the x position of the first pixel to get
+ * @param y the y position of the first pixel to get
+ * @param getWidth the width of the data to get
+ * @param pixels the buffer in which to put the pixels
+ * @param startIndex the offset into the byte array to begin storing pixels
+ *
+ * @exception IndexOutOfBoundsException if getWidth is too large
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8
+ *        (For higher depths, use the int[] version of this method.)</li>
+ * </ul>
+ */
+public void getPixels(int x, int y, int getWidth, byte[] pixels, int startIndex) {
+    if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (getWidth is 0) return;
+    int index;
+    int theByte;
+    int mask = 0;
+    int n = getWidth;
+    int i = startIndex;
+    int srcX = x, srcY = y;
+    switch (depth) {
+        case 8:
+            index = (y * bytesPerLine) + x;
+            for (int j = 0; j < getWidth; j++) {
+                pixels[i] = data[index];
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index++;
+                }
+            }
+            return;
+        case 4:
+            index = (y * bytesPerLine) + (x >> 1);
+            if ((x & 0x1) is 1) {
+                theByte = data[index] & 0xFF;
+                pixels[i] = cast(byte)(theByte & 0x0F);
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index++;
+                }
+            }
+            while (n > 1) {
+                theByte = data[index] & 0xFF;
+                pixels[i] = cast(byte)(theByte >> 4);
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    pixels[i] = cast(byte)(theByte & 0x0F);
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+                }
+            }
+            if (n > 0) {
+                theByte = data[index] & 0xFF;
+                pixels[i] = cast(byte)(theByte >> 4);
+            }
+            return;
+        case 2:
+            index = (y * bytesPerLine) + (x >> 2);
+            theByte = data[index] & 0xFF;
+            int offset;
+            while (n > 0) {
+                offset = 3 - (srcX % 4);
+                mask = 3 << (offset * 2);
+                pixels[i] = cast(byte)((theByte & mask) >> (offset * 2));
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    if (n > 0) theByte = data[index] & 0xFF;
+                    srcX = 0;
+                } else {
+                    if (offset is 0) {
+                        index++;
+                        theByte = data[index] & 0xFF;
+                    }
+                }
+            }
+            return;
+        case 1:
+            index = (y * bytesPerLine) + (x >> 3);
+            theByte = data[index] & 0xFF;
+            while (n > 0) {
+                mask = 1 << (7 - (srcX & 0x7));
+                if ((theByte & mask) is 0) {
+                    pixels[i] = 0;
+                } else {
+                    pixels[i] = 1;
+                }
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    if (n > 0) theByte = data[index] & 0xFF;
+                    srcX = 0;
+                } else {
+                    if (mask is 1) {
+                        index++;
+                        if (n > 0) theByte = data[index] & 0xFF;
+                    }
+                }
+            }
+            return;
+        default:
+    }
+    DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+}
+
+/**
+ * Returns <code>getWidth</code> pixel values starting at offset
+ * <code>x</code> in scanline <code>y</code> in the receiver's
+ * data starting at <code>startIndex</code>.
+ *
+ * @param x the x position of the first pixel to get
+ * @param y the y position of the first pixel to get
+ * @param getWidth the width of the data to get
+ * @param pixels the buffer in which to put the pixels
+ * @param startIndex the offset into the buffer to begin storing pixels
+ *
+ * @exception IndexOutOfBoundsException if getWidth is too large
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+ * </ul>
+ */
+public void getPixels(int x, int y, int getWidth, int[] pixels, int startIndex) {
+    if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (getWidth is 0) return;
+    int index;
+    int theByte;
+    int mask;
+    int n = getWidth;
+    int i = startIndex;
+    int srcX = x, srcY = y;
+    switch (depth) {
+        case 32:
+            index = (y * bytesPerLine) + (x * 4);
+            i = startIndex;
+            for (int j = 0; j < getWidth; j++) {
+                pixels[i] = ((data[index] & 0xFF) << 24) | ((data[index+1] & 0xFF) << 16)
+                    | ((data[index+2] & 0xFF) << 8) | (data[index+3] & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index += 4;
+                }
+            }
+            return;
+        case 24:
+            index = (y * bytesPerLine) + (x * 3);
+            for (int j = 0; j < getWidth; j++) {
+                pixels[i] = ((data[index] & 0xFF) << 16) | ((data[index+1] & 0xFF) << 8)
+                    | (data[index+2] & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index += 3;
+                }
+            }
+            return;
+        case 16:
+            index = (y * bytesPerLine) + (x * 2);
+            for (int j = 0; j < getWidth; j++) {
+                pixels[i] = ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index += 2;
+                }
+            }
+            return;
+        case 8:
+            index = (y * bytesPerLine) + x;
+            for (int j = 0; j < getWidth; j++) {
+                pixels[i] = data[index] & 0xFF;
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index++;
+                }
+            }
+            return;
+        case 4:
+            index = (y * bytesPerLine) + (x >> 1);
+            if ((x & 0x1) is 1) {
+                theByte = data[index] & 0xFF;
+                pixels[i] = theByte & 0x0F;
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index++;
+                }
+            }
+            while (n > 1) {
+                theByte = data[index] & 0xFF;
+                pixels[i] = theByte >> 4;
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    pixels[i] = theByte & 0x0F;
+                    i++;
+                    n--;
+                    srcX++;
+                    if (srcX >= width) {
+                        srcY++;
+                        index = srcY * bytesPerLine;
+                        srcX = 0;
+                    } else {
+                        index++;
+                    }
+                }
+            }
+            if (n > 0) {
+                theByte = data[index] & 0xFF;
+                pixels[i] = theByte >> 4;
+            }
+            return;
+        case 2:
+            index = (y * bytesPerLine) + (x >> 2);
+            theByte = data[index] & 0xFF;
+            int offset;
+            while (n > 0) {
+                offset = 3 - (srcX % 4);
+                mask = 3 << (offset * 2);
+                pixels[i] = cast(byte)((theByte & mask) >> (offset * 2));
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    if (n > 0) theByte = data[index] & 0xFF;
+                    srcX = 0;
+                } else {
+                    if (offset is 0) {
+                        index++;
+                        theByte = data[index] & 0xFF;
+                    }
+                }
+            }
+            return;
+        case 1:
+            index = (y * bytesPerLine) + (x >> 3);
+            theByte = data[index] & 0xFF;
+            while (n > 0) {
+                mask = 1 << (7 - (srcX & 0x7));
+                if ((theByte & mask) is 0) {
+                    pixels[i] = 0;
+                } else {
+                    pixels[i] = 1;
+                }
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    if (n > 0) theByte = data[index] & 0xFF;
+                    srcX = 0;
+                } else {
+                    if (mask is 1) {
+                        index++;
+                        if (n > 0) theByte = data[index] & 0xFF;
+                    }
+                }
+            }
+            return;
+        default:
+    }
+    DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+}
+
+/**
+ * Returns an array of <code>RGB</code>s which comprise the
+ * indexed color table of the receiver, or null if the receiver
+ * has a direct color model.
+ *
+ * @return the RGB values for the image or null if direct color
+ *
+ * @see PaletteData#getRGBs()
+ */
+public RGB[] getRGBs() {
+    return palette.getRGBs();
+}
+
+/**
+ * Returns an <code>ImageData</code> which specifies the
+ * transparency mask information for the receiver. If the
+ * receiver has no transparency or is not an icon, returns
+ * an opaque mask.
+ *
+ * @return the transparency mask
+ */
+public ImageData getTransparencyMask() {
+    if (getTransparencyType() is DWT.TRANSPARENCY_MASK) {
+        return new ImageData(width, height, 1, bwPalette(), maskPad, maskData);
+    } else {
+        return colorMaskImage(transparentPixel);
+    }
+}
+
+/**
+ * Returns the image transparency type, which will be one of
+ * <code>DWT.TRANSPARENCY_NONE</code>, <code>DWT.TRANSPARENCY_MASK</code>,
+ * <code>DWT.TRANSPARENCY_PIXEL</code> or <code>DWT.TRANSPARENCY_ALPHA</code>.
+ *
+ * @return the receiver's transparency type
+ */
+public int getTransparencyType() {
+    if (maskData !is null) return DWT.TRANSPARENCY_MASK;
+    if (transparentPixel !is -1) return DWT.TRANSPARENCY_PIXEL;
+    if (alphaData !is null) return DWT.TRANSPARENCY_ALPHA;
+    return DWT.TRANSPARENCY_NONE;
+}
+
+/**
+ * Returns the byte order of the receiver.
+ *
+ * @return MSB_FIRST or LSB_FIRST
+ */
+int getByteOrder() {
+    return depth !is 16 ? MSB_FIRST : LSB_FIRST;
+}
+
+/**
+ * Returns a copy of the receiver which has been stretched or
+ * shrunk to the specified size. If either the width or height
+ * is negative, the resulting image will be inverted in the
+ * associated axis.
+ *
+ * @param width the width of the new ImageData
+ * @param height the height of the new ImageData
+ * @return a scaled copy of the image
+ */
+public ImageData scaledTo(int width, int height) {
+    /* Create a destination image with no data */
+    bool flipX = (width < 0);
+    if (flipX) width = - width;
+    bool flipY = (height < 0);
+    if (flipY) height = - height;
+
+    ImageData dest = new ImageData(
+        width, height, depth, palette,
+        scanlinePad, null, 0, null,
+        null, -1, transparentPixel, type,
+        x, y, disposalMethod, delayTime);
+
+    /* Scale the image contents */
+    if (palette.isDirect) blit(BLIT_SRC,
+        this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, 0, 0, 0,
+        ALPHA_OPAQUE, null, 0, 0, 0,
+        dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, 0, 0, 0,
+        flipX, flipY);
+    else blit(BLIT_SRC,
+        this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, null, null, null,
+        ALPHA_OPAQUE, null, 0, 0, 0,
+        dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, null, null, null,
+        flipX, flipY);
+
+    /* Scale the image mask or alpha */
+    if (maskData !is null) {
+        dest.maskPad = this.maskPad;
+        int destBpl = (dest.width + 7) / 8;
+        destBpl = (destBpl + (dest.maskPad - 1)) / dest.maskPad * dest.maskPad;
+        dest.maskData = new byte[destBpl * dest.height];
+        int srcBpl = (this.width + 7) / 8;
+        srcBpl = (srcBpl + (this.maskPad - 1)) / this.maskPad * this.maskPad;
+        blit(BLIT_SRC,
+            this.maskData, 1, srcBpl, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
+            ALPHA_OPAQUE, null, 0, 0, 0,
+            dest.maskData, 1, destBpl, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
+            flipX, flipY);
+    } else if (alpha !is -1) {
+        dest.alpha = this.alpha;
+    } else if (alphaData !is null) {
+        dest.alphaData = new byte[dest.width * dest.height];
+        blit(BLIT_SRC,
+            this.alphaData, 8, this.width, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
+            ALPHA_OPAQUE, null, 0, 0, 0,
+            dest.alphaData, 8, dest.width, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
+            flipX, flipY);
+    }
+    return dest;
+}
+
+/**
+ * Sets the alpha value at offset <code>x</code> in
+ * scanline <code>y</code> in the receiver's alpha data.
+ *
+ * @param x the x coordinate of the alpha value to set
+ * @param y the y coordinate of the alpha value to set
+ * @param alpha the value to set the alpha to
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ *  </ul>
+ */
+public void setAlpha(int x, int y, int alpha) {
+    if (x >= width || y >= height || x < 0 || y < 0 || alpha < 0 || alpha > 255)
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+
+    if (alphaData is null) alphaData = new byte[width * height];
+    alphaData[y * width + x] = cast(byte)alpha;
+}
+
+/**
+ * Sets the alpha values starting at offset <code>x</code> in
+ * scanline <code>y</code> in the receiver's alpha data to the
+ * values from the array <code>alphas</code> starting at
+ * <code>startIndex</code>.
+ *
+ * @param x the x coordinate of the pixel to being setting the alpha values
+ * @param y the y coordinate of the pixel to being setting the alpha values
+ * @param putWidth the width of the alpha values to set
+ * @param alphas the alpha values to set
+ * @param startIndex the index at which to begin setting
+ *
+ * @exception IndexOutOfBoundsException if putWidth is too large
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
+ * </ul>
+ */
+public void setAlphas(int x, int y, int putWidth, byte[] alphas, int startIndex) {
+    if (alphas is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (putWidth is 0) return;
+
+    if (alphaData is null) alphaData = new byte[width * height];
+    // may throw an IndexOutOfBoundsException
+    System.arraycopy(alphas, startIndex, alphaData, y * width + x, putWidth);
+}
+
+/**
+ * Sets the pixel value at offset <code>x</code> in
+ * scanline <code>y</code> in the receiver's data.
+ *
+ * @param x the x coordinate of the pixel to set
+ * @param y the y coordinate of the pixel to set
+ * @param pixelValue the value to set the pixel to
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+ * </ul>
+ */
+public void setPixel(int x, int y, int pixelValue) {
+    if (x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    int index;
+    byte theByte;
+    int mask;
+    switch (depth) {
+        case 32:
+            index = (y * bytesPerLine) + (x * 4);
+            data[index]  = cast(byte)((pixelValue >> 24) & 0xFF);
+            data[index + 1] = cast(byte)((pixelValue >> 16) & 0xFF);
+            data[index + 2] = cast(byte)((pixelValue >> 8) & 0xFF);
+            data[index + 3] = cast(byte)(pixelValue & 0xFF);
+            return;
+        case 24:
+            index = (y * bytesPerLine) + (x * 3);
+            data[index] = cast(byte)((pixelValue >> 16) & 0xFF);
+            data[index + 1] = cast(byte)((pixelValue >> 8) & 0xFF);
+            data[index + 2] = cast(byte)(pixelValue & 0xFF);
+            return;
+        case 16:
+            index = (y * bytesPerLine) + (x * 2);
+            data[index + 1] = cast(byte)((pixelValue >> 8) & 0xFF);
+            data[index] = cast(byte)(pixelValue & 0xFF);
+            return;
+        case 8:
+            index = (y * bytesPerLine) + x ;
+            data[index] = cast(byte)(pixelValue & 0xFF);
+            return;
+        case 4:
+            index = (y * bytesPerLine) + (x >> 1);
+            if ((x & 0x1) is 0) {
+                data[index] = cast(byte)((data[index] & 0x0F) | ((pixelValue & 0x0F) << 4));
+            } else {
+                data[index] = cast(byte)((data[index] & 0xF0) | (pixelValue & 0x0F));
+            }
+            return;
+        case 2:
+            index = (y * bytesPerLine) + (x >> 2);
+            theByte = data[index];
+            int offset = 3 - (x % 4);
+            mask = 0xFF ^ (3 << (offset * 2));
+            data[index] = cast(byte)((data[index] & mask) | (pixelValue << (offset * 2)));
+            return;
+        case 1:
+            index = (y * bytesPerLine) + (x >> 3);
+            theByte = data[index];
+            mask = 1 << (7 - (x & 0x7));
+            if ((pixelValue & 0x1) is 1) {
+                data[index] = cast(byte)(theByte | mask);
+            } else {
+                data[index] = cast(byte)(theByte & (mask ^ -1));
+            }
+            return;
+        default:
+    }
+    DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+}
+
+/**
+ * Sets the pixel values starting at offset <code>x</code> in
+ * scanline <code>y</code> in the receiver's data to the
+ * values from the array <code>pixels</code> starting at
+ * <code>startIndex</code>.
+ *
+ * @param x the x position of the pixel to set
+ * @param y the y position of the pixel to set
+ * @param putWidth the width of the pixels to set
+ * @param pixels the pixels to set
+ * @param startIndex the index at which to begin setting
+ *
+ * @exception IndexOutOfBoundsException if putWidth is too large
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8
+ *        (For higher depths, use the int[] version of this method.)</li>
+ * </ul>
+ */
+public void setPixels(int x, int y, int putWidth, byte[] pixels, int startIndex) {
+    if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (putWidth is 0) return;
+    int index;
+    int theByte;
+    int mask;
+    int n = putWidth;
+    int i = startIndex;
+    int srcX = x, srcY = y;
+    switch (depth) {
+        case 8:
+            index = (y * bytesPerLine) + x;
+            for (int j = 0; j < putWidth; j++) {
+                data[index] = cast(byte)(pixels[i] & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index++;
+                }
+            }
+            return;
+        case 4:
+            index = (y * bytesPerLine) + (x >> 1);
+            bool high = (x & 0x1) is 0;
+            while (n > 0) {
+                theByte = pixels[i] & 0x0F;
+                if (high) {
+                    data[index] = cast(byte)((data[index] & 0x0F) | (theByte << 4));
+                } else {
+                    data[index] = cast(byte)((data[index] & 0xF0) | theByte);
+                }
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    high = true;
+                    srcX = 0;
+                } else {
+                    if (!high) index++;
+                    high = !high;
+                }
+            }
+            return;
+        case 2:
+            byte [] masks = [ cast(byte)0xFC, cast(byte)0xF3, cast(byte)0xCF, cast(byte)0x3F ];
+            index = (y * bytesPerLine) + (x >> 2);
+            int offset = 3 - (x % 4);
+            while (n > 0) {
+                theByte = pixels[i] & 0x3;
+                data[index] = cast(byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    offset = 0;
+                    srcX = 0;
+                } else {
+                    if (offset is 0) {
+                        index++;
+                        offset = 3;
+                    } else {
+                        offset--;
+                    }
+                }
+            }
+            return;
+        case 1:
+            index = (y * bytesPerLine) + (x >> 3);
+            while (n > 0) {
+                mask = 1 << (7 - (srcX & 0x7));
+                if ((pixels[i] & 0x1) is 1) {
+                    data[index] = cast(byte)((data[index] & 0xFF) | mask);
+                } else {
+                    data[index] = cast(byte)((data[index] & 0xFF) & (mask ^ -1));
+                }
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    if (mask is 1) {
+                        index++;
+                    }
+                }
+            }
+            return;
+        default:
+    }
+    DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+}
+
+/**
+ * Sets the pixel values starting at offset <code>x</code> in
+ * scanline <code>y</code> in the receiver's data to the
+ * values from the array <code>pixels</code> starting at
+ * <code>startIndex</code>.
+ *
+ * @param x the x position of the pixel to set
+ * @param y the y position of the pixel to set
+ * @param putWidth the width of the pixels to set
+ * @param pixels the pixels to set
+ * @param startIndex the index at which to begin setting
+ *
+ * @exception IndexOutOfBoundsException if putWidth is too large
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
+ * </ul>
+ */
+public void setPixels(int x, int y, int putWidth, int[] pixels, int startIndex) {
+    if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (putWidth is 0) return;
+    int index;
+    int theByte;
+    int mask;
+    int n = putWidth;
+    int i = startIndex;
+    int pixel;
+    int srcX = x, srcY = y;
+    switch (depth) {
+        case 32:
+            index = (y * bytesPerLine) + (x * 4);
+            for (int j = 0; j < putWidth; j++) {
+                pixel = pixels[i];
+                data[index] = cast(byte)((pixel >> 24) & 0xFF);
+                data[index + 1] = cast(byte)((pixel >> 16) & 0xFF);
+                data[index + 2] = cast(byte)((pixel >> 8) & 0xFF);
+                data[index + 3] = cast(byte)(pixel & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index += 4;
+                }
+            }
+            return;
+        case 24:
+            index = (y * bytesPerLine) + (x * 3);
+            for (int j = 0; j < putWidth; j++) {
+                pixel = pixels[i];
+                data[index] = cast(byte)((pixel >> 16) & 0xFF);
+                data[index + 1] = cast(byte)((pixel >> 8) & 0xFF);
+                data[index + 2] = cast(byte)(pixel & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index += 3;
+                }
+            }
+            return;
+        case 16:
+            index = (y * bytesPerLine) + (x * 2);
+            for (int j = 0; j < putWidth; j++) {
+                pixel = pixels[i];
+                data[index] = cast(byte)(pixel & 0xFF);
+                data[index + 1] = cast(byte)((pixel >> 8) & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index += 2;
+                }
+            }
+            return;
+        case 8:
+            index = (y * bytesPerLine) + x;
+            for (int j = 0; j < putWidth; j++) {
+                data[index] = cast(byte)(pixels[i] & 0xFF);
+                i++;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    index++;
+                }
+            }
+            return;
+        case 4:
+            index = (y * bytesPerLine) + (x >> 1);
+            bool high = (x & 0x1) is 0;
+            while (n > 0) {
+                theByte = pixels[i] & 0x0F;
+                if (high) {
+                    data[index] = cast(byte)((data[index] & 0x0F) | (theByte << 4));
+                } else {
+                    data[index] = cast(byte)((data[index] & 0xF0) | theByte);
+                }
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    high = true;
+                    srcX = 0;
+                } else {
+                    if (!high) index++;
+                    high = !high;
+                }
+            }
+            return;
+        case 2:
+            byte [] masks = [ cast(byte)0xFC, cast(byte)0xF3, cast(byte)0xCF, cast(byte)0x3F ];
+            index = (y * bytesPerLine) + (x >> 2);
+            int offset = 3 - (x % 4);
+            while (n > 0) {
+                theByte = pixels[i] & 0x3;
+                data[index] = cast(byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    offset = 3;
+                    srcX = 0;
+                } else {
+                    if (offset is 0) {
+                        index++;
+                        offset = 3;
+                    } else {
+                        offset--;
+                    }
+                }
+            }
+            return;
+        case 1:
+            index = (y * bytesPerLine) + (x >> 3);
+            while (n > 0) {
+                mask = 1 << (7 - (srcX & 0x7));
+                if ((pixels[i] & 0x1) is 1) {
+                    data[index] = cast(byte)((data[index] & 0xFF) | mask);
+                } else {
+                    data[index] = cast(byte)((data[index] & 0xFF) & (mask ^ -1));
+                }
+                i++;
+                n--;
+                srcX++;
+                if (srcX >= width) {
+                    srcY++;
+                    index = srcY * bytesPerLine;
+                    srcX = 0;
+                } else {
+                    if (mask is 1) {
+                        index++;
+                    }
+                }
+            }
+            return;
+        default:
+    }
+    DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
+}
+
+/**
+ * Returns a palette with 2 colors: black & white.
+ */
+static PaletteData bwPalette() {
+    return new PaletteData( [ new RGB(0, 0, 0), new RGB(255, 255, 255) ] );
+}
+
+/**
+ * Gets the offset of the most significant bit for
+ * the given mask.
+ */
+static int getMSBOffset(int mask) {
+    for (int i = 31; i >= 0; i--) {
+        if (((mask >> i) & 0x1) !is 0) return i + 1;
+    }
+    return 0;
+}
+
+/**
+ * Finds the closest match.
+ */
+static int closestMatch(int depth, byte red, byte green, byte blue, int redMask, int greenMask, int blueMask, byte[] reds, byte[] greens, byte[] blues) {
+    if (depth > 8) {
+        int rshift = 32 - getMSBOffset(redMask);
+        int gshift = 32 - getMSBOffset(greenMask);
+        int bshift = 32 - getMSBOffset(blueMask);
+        return (((red << 24) >>> rshift) & redMask) |
+            (((green << 24) >>> gshift) & greenMask) |
+            (((blue << 24) >>> bshift) & blueMask);
+    }
+    int r, g, b;
+    int minDistance = 0x7fffffff;
+    int nearestPixel = 0;
+    int n = reds.length;
+    for (int j = 0; j < n; j++) {
+        r = (reds[j] & 0xFF) - (red & 0xFF);
+        g = (greens[j] & 0xFF) - (green & 0xFF);
+        b = (blues[j] & 0xFF) - (blue & 0xFF);
+        int distance = r*r + g*g + b*b;
+        if (distance < minDistance) {
+            nearestPixel = j;
+            if (distance is 0) break;
+            minDistance = distance;
+        }
+    }
+    return nearestPixel;
+}
+
+static final ImageData convertMask(ImageData mask) {
+    if (mask.depth is 1) return mask;
+    PaletteData palette = new PaletteData([new RGB(0, 0, 0), new RGB(255,255,255)]);
+    ImageData newMask = new ImageData(mask.width, mask.height, 1, palette);
+    /* Find index of black in mask palette */
+    int blackIndex = 0;
+    RGB[] rgbs = mask.getRGBs();
+    if (rgbs !is null) {
+        while (blackIndex < rgbs.length) {
+            if (rgbs[blackIndex] is palette.colors[0] ) break;
+            blackIndex++;
+        }
+    }
+    int[] pixels = new int[mask.width];
+    for (int y = 0; y < mask.height; y++) {
+        mask.getPixels(0, y, mask.width, pixels, 0);
+        for (int i = 0; i < pixels.length; i++) {
+            if (pixels[i] is blackIndex) {
+                pixels[i] = 0;
+            } else {
+                pixels[i] = 1;
+            }
+        }
+        newMask.setPixels(0, y, mask.width, pixels, 0);
+    }
+    return newMask;
+}
+
+static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
+    if (pad is 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++) {
+        System.arraycopy(data, srcIndex, newData, destIndex, stride);
+        srcIndex += bpl;
+        destIndex += newBpl;
+    }
+    return newData;
+}
+
+/**
+ * Blit operation bits to be OR'ed together to specify the desired operation.
+ */
+static const int
+    BLIT_SRC = 1,     // copy source directly, else applies logic operations
+    BLIT_ALPHA = 2,   // enable alpha blending
+    BLIT_DITHER = 4;  // enable dithering in low color modes
+
+/**
+ * Alpha mode, values 0 - 255 specify global alpha level
+ */
+static const int
+    ALPHA_OPAQUE = 255,           // Fully opaque (ignores any alpha data)
+    ALPHA_TRANSPARENT = 0,        // Fully transparent (ignores any alpha data)
+    ALPHA_CHANNEL_SEPARATE = -1,  // Use alpha channel from separate alphaData
+    ALPHA_CHANNEL_SOURCE = -2,    // Use alpha channel embedded in sourceData
+    ALPHA_MASK_UNPACKED = -3,     // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
+    ALPHA_MASK_PACKED = -4,       // Use transparency mask formed by packed bits in alphaData
+    ALPHA_MASK_INDEX = -5,        // Consider source palette indices transparent if in alphaData array
+    ALPHA_MASK_RGB = -6;          // Consider source RGBs transparent if in RGB888 format alphaData array
+
+/**
+ * Byte and bit order constants.
+ */
+static const int LSB_FIRST = 0;
+static const int MSB_FIRST = 1;
+
+/**
+ * Data types (internal)
+ */
+private static const int
+    // direct / true color formats with arbitrary masks & shifts
+    TYPE_GENERIC_8 = 0,
+    TYPE_GENERIC_16_MSB = 1,
+    TYPE_GENERIC_16_LSB = 2,
+    TYPE_GENERIC_24 = 3,
+    TYPE_GENERIC_32_MSB = 4,
+    TYPE_GENERIC_32_LSB = 5,
+    // palette indexed color formats
+    TYPE_INDEX_8 = 6,
+    TYPE_INDEX_4 = 7,
+    TYPE_INDEX_2 = 8,
+    TYPE_INDEX_1_MSB = 9,
+    TYPE_INDEX_1_LSB = 10;
+
+/**
+ * Blits a direct palette image into a direct palette image.
+ * <p>
+ * Note: When the source and destination depth, order and masks
+ * are pairwise equal and the blitter operation is BLIT_SRC,
+ * the masks are ignored.  Hence when not changing the image
+ * data format, 0 may be specified for the masks.
+ * </p>
+ *
+ * @param op the blitter operation: a combination of BLIT_xxx flags
+ *        (see BLIT_xxx constants)
+ * @param srcData the source byte array containing image data
+ * @param srcDepth the source depth: one of 8, 16, 24, 32
+ * @param srcStride the source number of bytes per line
+ * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if srcDepth is not 16 or 32
+ * @param srcX the top-left x-coord of the source blit region
+ * @param srcY the top-left y-coord of the source blit region
+ * @param srcWidth the width of the source blit region
+ * @param srcHeight the height of the source blit region
+ * @param srcRedMask the source red channel mask
+ * @param srcGreenMask the source green channel mask
+ * @param srcBlueMask the source blue channel mask
+ * @param alphaMode the alpha blending or mask mode, may be
+ *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+ *        not specified in the blitter operations
+ *        (see ALPHA_MODE_xxx constants)
+ * @param alphaData the alpha blending or mask data, varies depending
+ *        on the value of alphaMode and sometimes ignored
+ * @param alphaStride the alpha data number of bytes per line
+ * @param alphaX the top-left x-coord of the alpha blit region
+ * @param alphaY the top-left y-coord of the alpha blit region
+ * @param destData the destination byte array containing image data
+ * @param destDepth the destination depth: one of 8, 16, 24, 32
+ * @param destStride the destination number of bytes per line
+ * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if destDepth is not 16 or 32
+ * @param destX the top-left x-coord of the destination blit region
+ * @param destY the top-left y-coord of the destination blit region
+ * @param destWidth the width of the destination blit region
+ * @param destHeight the height of the destination blit region
+ * @param destRedMask the destination red channel mask
+ * @param destGreenMask the destination green channel mask
+ * @param destBlueMask the destination blue channel mask
+ * @param flipX if true the resulting image is flipped along the vertical axis
+ * @param flipY if true the resulting image is flipped along the horizontal axis
+ */
+static void blit(int op,
+    byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+    int srcX, int srcY, int srcWidth, int srcHeight,
+    int srcRedMask, int srcGreenMask, int srcBlueMask,
+    int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+    byte[] destData, int destDepth, int destStride, int destOrder,
+    int destX, int destY, int destWidth, int destHeight,
+    int destRedMask, int destGreenMask, int destBlueMask,
+    bool flipX, bool flipY) {
+
+    static_this();
+
+    if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
+
+    // these should be supplied as params later
+    const int srcAlphaMask = 0, destAlphaMask = 0;
+
+    /*** Prepare scaling data ***/
+    int dwm1 = destWidth - 1;
+    int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0;
+    int dhm1 = destHeight - 1;
+    int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0;
+
+    /*** Prepare source-related data ***/
+    int sbpp, stype;
+    switch (srcDepth) {
+        case 8:
+            sbpp = 1;
+            stype = TYPE_GENERIC_8;
+            break;
+        case 16:
+            sbpp = 2;
+            stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+            break;
+        case 24:
+            sbpp = 3;
+            stype = TYPE_GENERIC_24;
+            break;
+        case 32:
+            sbpp = 4;
+            stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid source type");
+            return;
+    }
+    int spr = srcY * srcStride + srcX * sbpp;
+
+    /*** Prepare destination-related data ***/
+    int dbpp, dtype;
+    switch (destDepth) {
+        case 8:
+            dbpp = 1;
+            dtype = TYPE_GENERIC_8;
+            break;
+        case 16:
+            dbpp = 2;
+            dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+            break;
+        case 24:
+            dbpp = 3;
+            dtype = TYPE_GENERIC_24;
+            break;
+        case 32:
+            dbpp = 4;
+            dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid destination type");
+            return;
+    }
+    int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
+    int dprxi = (flipX) ? -dbpp : dbpp;
+    int dpryi = (flipY) ? -destStride : destStride;
+
+    /*** Prepare special processing data ***/
+    int apr;
+    if ((op & BLIT_ALPHA) !is 0) {
+        switch (alphaMode) {
+            case ALPHA_MASK_UNPACKED:
+            case ALPHA_CHANNEL_SEPARATE:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_PACKED:
+                if (alphaData is null) alphaMode = 0x10000;
+                alphaStride <<= 3;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_INDEX:
+                //throw new IllegalArgumentException("Invalid alpha type");
+                return;
+            case ALPHA_MASK_RGB:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = 0;
+                break;
+            default:
+                alphaMode = (alphaMode << 16) / 255; // prescale
+            case ALPHA_CHANNEL_SOURCE:
+                apr = 0;
+                break;
+        }
+    } else {
+        alphaMode = 0x10000;
+        apr = 0;
+    }
+
+    /*** Blit ***/
+    int dp = dpr;
+    int sp = spr;
+    if ((alphaMode is 0x10000) && (stype is dtype) &&
+        (srcRedMask is destRedMask) && (srcGreenMask is destGreenMask) &&
+        (srcBlueMask is destBlueMask) && (srcAlphaMask is destAlphaMask)) {
+        /*** Fast blit (straight copy) ***/
+        switch (sbpp) {
+            case 1:
+                for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                    for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                        destData[dp] = srcData[sp];
+                        sp += (sfx >>> 16);
+                    }
+                }
+                break;
+            case 2:
+                for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                    for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                        destData[dp] = srcData[sp];
+                        destData[dp + 1] = srcData[sp + 1];
+                        sp += (sfx >>> 16) * 2;
+                    }
+                }
+                break;
+            case 3:
+                for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                    for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                        destData[dp] = srcData[sp];
+                        destData[dp + 1] = srcData[sp + 1];
+                        destData[dp + 2] = srcData[sp + 2];
+                        sp += (sfx >>> 16) * 3;
+                    }
+                }
+                break;
+            case 4:
+                for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                    for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                        destData[dp] = srcData[sp];
+                        destData[dp + 1] = srcData[sp + 1];
+                        destData[dp + 2] = srcData[sp + 2];
+                        destData[dp + 3] = srcData[sp + 3];
+                        sp += (sfx >>> 16) * 4;
+                    }
+                }
+                break;
+        default:
+        }
+        return;
+    }
+    /*** Comprehensive blit (apply transformations) ***/
+    int srcRedShift = getChannelShift(srcRedMask);
+    byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
+    int srcGreenShift = getChannelShift(srcGreenMask);
+    byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
+    int srcBlueShift = getChannelShift(srcBlueMask);
+    byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
+    int srcAlphaShift = getChannelShift(srcAlphaMask);
+    byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
+
+    int destRedShift = getChannelShift(destRedMask);
+    int destRedWidth = getChannelWidth(destRedMask, destRedShift);
+    byte[] destReds = ANY_TO_EIGHT[destRedWidth];
+    int destRedPreShift = 8 - destRedWidth;
+    int destGreenShift = getChannelShift(destGreenMask);
+    int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
+    byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
+    int destGreenPreShift = 8 - destGreenWidth;
+    int destBlueShift = getChannelShift(destBlueMask);
+    int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
+    byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
+    int destBluePreShift = 8 - destBlueWidth;
+    int destAlphaShift = getChannelShift(destAlphaMask);
+    int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
+    byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
+    int destAlphaPreShift = 8 - destAlphaWidth;
+
+    int ap = apr, alpha = alphaMode;
+    int r = 0, g = 0, b = 0, a = 0;
+    int rq = 0, gq = 0, bq = 0, aq = 0;
+    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+            sp = spr += (sfy >>> 16) * srcStride,
+            ap = apr += (sfy >>> 16) * alphaStride,
+            sfy = (sfy & 0xffff) + sfyi,
+            dp = dpr += dpryi) {
+        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                dp += dprxi,
+                sfx = (sfx & 0xffff) + sfxi) {
+            /*** READ NEXT PIXEL ***/
+            switch (stype) {
+                case TYPE_GENERIC_8: {
+                    int data = srcData[sp] & 0xff;
+                    sp += (sfx >>> 16);
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_16_MSB: {
+                    int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
+                    sp += (sfx >>> 16) * 2;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_16_LSB: {
+                    int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
+                    sp += (sfx >>> 16) * 2;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_24: {
+                    int data = (( ((srcData[sp] & 0xff) << 8) |
+                        (srcData[sp + 1] & 0xff)) << 8) |
+                        (srcData[sp + 2] & 0xff);
+                    sp += (sfx >>> 16) * 3;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_32_MSB: {
+                    int data = (( (( ((srcData[sp] & 0xff) << 8) |
+                        (srcData[sp + 1] & 0xff)) << 8) |
+                        (srcData[sp + 2] & 0xff)) << 8) |
+                        (srcData[sp + 3] & 0xff);
+                    sp += (sfx >>> 16) * 4;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_32_LSB: {
+                    int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
+                        (srcData[sp + 2] & 0xff)) << 8) |
+                        (srcData[sp + 1] & 0xff)) << 8) |
+                        (srcData[sp] & 0xff);
+                    sp += (sfx >>> 16) * 4;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                default:
+            }
+
+            /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+            switch (alphaMode) {
+                case ALPHA_CHANNEL_SEPARATE:
+                    alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_CHANNEL_SOURCE:
+                    alpha = (a << 16) / 255;
+                    break;
+                case ALPHA_MASK_UNPACKED:
+                    alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_PACKED:
+                    alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_RGB:
+                    alpha = 0x10000;
+                    for (int i = 0; i < alphaData.length; i += 3) {
+                        if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) {
+                            alpha = 0x0000;
+                            break;
+                        }
+                    }
+                    break;
+                default:
+            }
+            if (alpha !is 0x10000) {
+                if (alpha is 0x0000) continue;
+                switch (dtype) {
+                    case TYPE_GENERIC_8: {
+                        int data = destData[dp] & 0xff;
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_16_MSB: {
+                        int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_16_LSB: {
+                        int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_24: {
+                        int data = (( ((destData[dp] & 0xff) << 8) |
+                            (destData[dp + 1] & 0xff)) << 8) |
+                            (destData[dp + 2] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_32_MSB: {
+                        int data = (( (( ((destData[dp] & 0xff) << 8) |
+                            (destData[dp + 1] & 0xff)) << 8) |
+                            (destData[dp + 2] & 0xff)) << 8) |
+                            (destData[dp + 3] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_32_LSB: {
+                        int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
+                            (destData[dp + 2] & 0xff)) << 8) |
+                            (destData[dp + 1] & 0xff)) << 8) |
+                            (destData[dp] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    default:
+                }
+                // Perform alpha blending
+                a = aq + ((a - aq) * alpha >> 16);
+                r = rq + ((r - rq) * alpha >> 16);
+                g = gq + ((g - gq) * alpha >> 16);
+                b = bq + ((b - bq) * alpha >> 16);
+            }
+
+            /*** WRITE NEXT PIXEL ***/
+            int data =
+                (r >>> destRedPreShift << destRedShift) |
+                (g >>> destGreenPreShift << destGreenShift) |
+                (b >>> destBluePreShift << destBlueShift) |
+                (a >>> destAlphaPreShift << destAlphaShift);
+            switch (dtype) {
+                case TYPE_GENERIC_8: {
+                    destData[dp] = cast(byte) data;
+                } break;
+                case TYPE_GENERIC_16_MSB: {
+                    destData[dp] = cast(byte) (data >>> 8);
+                    destData[dp + 1] = cast(byte) (data & 0xff);
+                } break;
+                case TYPE_GENERIC_16_LSB: {
+                    destData[dp] = cast(byte) (data & 0xff);
+                    destData[dp + 1] = cast(byte) (data >>> 8);
+                } break;
+                case TYPE_GENERIC_24: {
+                    destData[dp] = cast(byte) (data >>> 16);
+                    destData[dp + 1] = cast(byte) (data >>> 8);
+                    destData[dp + 2] = cast(byte) (data & 0xff);
+                } break;
+                case TYPE_GENERIC_32_MSB: {
+                    destData[dp] = cast(byte) (data >>> 24);
+                    destData[dp + 1] = cast(byte) (data >>> 16);
+                    destData[dp + 2] = cast(byte) (data >>> 8);
+                    destData[dp + 3] = cast(byte) (data & 0xff);
+                } break;
+                case TYPE_GENERIC_32_LSB: {
+                    destData[dp] = cast(byte) (data & 0xff);
+                    destData[dp + 1] = cast(byte) (data >>> 8);
+                    destData[dp + 2] = cast(byte) (data >>> 16);
+                    destData[dp + 3] = cast(byte) (data >>> 24);
+                } break;
+                default:
+            }
+        }
+    }
+}
+
+/**
+ * Blits an index palette image into an index palette image.
+ * <p>
+ * Note: The source and destination red, green, and blue
+ * arrays may be null if no alpha blending or dither is to be
+ * performed.
+ * </p>
+ *
+ * @param op the blitter operation: a combination of BLIT_xxx flags
+ *        (see BLIT_xxx constants)
+ * @param srcData the source byte array containing image data
+ * @param srcDepth the source depth: one of 1, 2, 4, 8
+ * @param srcStride the source number of bytes per line
+ * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if srcDepth is not 1
+ * @param srcX the top-left x-coord of the source blit region
+ * @param srcY the top-left y-coord of the source blit region
+ * @param srcWidth the width of the source blit region
+ * @param srcHeight the height of the source blit region
+ * @param srcReds the source palette red component intensities
+ * @param srcGreens the source palette green component intensities
+ * @param srcBlues the source palette blue component intensities
+ * @param alphaMode the alpha blending or mask mode, may be
+ *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+ *        not specified in the blitter operations
+ *        (see ALPHA_MODE_xxx constants)
+ * @param alphaData the alpha blending or mask data, varies depending
+ *        on the value of alphaMode and sometimes ignored
+ * @param alphaStride the alpha data number of bytes per line
+ * @param alphaX the top-left x-coord of the alpha blit region
+ * @param alphaY the top-left y-coord of the alpha blit region
+ * @param destData the destination byte array containing image data
+ * @param destDepth the destination depth: one of 1, 2, 4, 8
+ * @param destStride the destination number of bytes per line
+ * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if destDepth is not 1
+ * @param destX the top-left x-coord of the destination blit region
+ * @param destY the top-left y-coord of the destination blit region
+ * @param destWidth the width of the destination blit region
+ * @param destHeight the height of the destination blit region
+ * @param destReds the destination palette red component intensities
+ * @param destGreens the destination palette green component intensities
+ * @param destBlues the destination palette blue component intensities
+ * @param flipX if true the resulting image is flipped along the vertical axis
+ * @param flipY if true the resulting image is flipped along the horizontal axis
+ */
+static void blit(int op,
+    byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+    int srcX, int srcY, int srcWidth, int srcHeight,
+    byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
+    int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+    byte[] destData, int destDepth, int destStride, int destOrder,
+    int destX, int destY, int destWidth, int destHeight,
+    byte[] destReds, byte[] destGreens, byte[] destBlues,
+    bool flipX, bool flipY) {
+
+    static_this();
+
+    if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
+
+    /*** Prepare scaling data ***/
+    int dwm1 = destWidth - 1;
+    int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0;
+    int dhm1 = destHeight - 1;
+    int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0;
+
+    /*** Prepare source-related data ***/
+    int stype;
+    switch (srcDepth) {
+        case 8:
+            stype = TYPE_INDEX_8;
+            break;
+        case 4:
+            srcStride <<= 1;
+            stype = TYPE_INDEX_4;
+            break;
+        case 2:
+            srcStride <<= 2;
+            stype = TYPE_INDEX_2;
+            break;
+        case 1:
+            srcStride <<= 3;
+            stype = (srcOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid source type");
+            return;
+    }
+    int spr = srcY * srcStride + srcX;
+
+    /*** Prepare destination-related data ***/
+    int dtype;
+    switch (destDepth) {
+        case 8:
+            dtype = TYPE_INDEX_8;
+            break;
+        case 4:
+            destStride <<= 1;
+            dtype = TYPE_INDEX_4;
+            break;
+        case 2:
+            destStride <<= 2;
+            dtype = TYPE_INDEX_2;
+            break;
+        case 1:
+            destStride <<= 3;
+            dtype = (destOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid source type");
+            return;
+    }
+    int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
+    int dprxi = (flipX) ? -1 : 1;
+    int dpryi = (flipY) ? -destStride : destStride;
+
+    /*** Prepare special processing data ***/
+    int apr;
+    if ((op & BLIT_ALPHA) !is 0) {
+        switch (alphaMode) {
+            case ALPHA_MASK_UNPACKED:
+            case ALPHA_CHANNEL_SEPARATE:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_PACKED:
+                if (alphaData is null) alphaMode = 0x10000;
+                alphaStride <<= 3;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_INDEX:
+            case ALPHA_MASK_RGB:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = 0;
+                break;
+            default:
+                alphaMode = (alphaMode << 16) / 255; // prescale
+            case ALPHA_CHANNEL_SOURCE:
+                apr = 0;
+                break;
+        }
+    } else {
+        alphaMode = 0x10000;
+        apr = 0;
+    }
+    bool ditherEnabled = (op & BLIT_DITHER) !is 0;
+
+    /*** Blit ***/
+    int dp = dpr;
+    int sp = spr;
+    int ap = apr;
+    int destPaletteSize = 1 << destDepth;
+    if ((destReds !is null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
+    byte[] paletteMapping = null;
+    bool isExactPaletteMapping = true;
+    switch (alphaMode) {
+        case 0x10000:
+            /*** If the palettes and formats are equivalent use a one-to-one mapping ***/
+            if ((stype is dtype) &&
+                (srcReds is destReds) && (srcGreens is destGreens) && (srcBlues is destBlues)) {
+                paletteMapping = ONE_TO_ONE_MAPPING;
+                break;
+            /*** If palettes have not been supplied, supply a suitable mapping ***/
+            } else if ((srcReds is null) || (destReds is null)) {
+                if (srcDepth <= destDepth) {
+                    paletteMapping = ONE_TO_ONE_MAPPING;
+                } else {
+                    paletteMapping = new byte[1 << srcDepth];
+                    int mask = (0xff << destDepth) >>> 8;
+                    for (int i = 0; i < paletteMapping.length; ++i) paletteMapping[i] = cast(byte)(i & mask);
+                }
+                break;
+            }
+        case ALPHA_MASK_UNPACKED:
+        case ALPHA_MASK_PACKED:
+        case ALPHA_MASK_INDEX:
+        case ALPHA_MASK_RGB:
+            /*** Generate a palette mapping ***/
+            int srcPaletteSize = 1 << srcDepth;
+            paletteMapping = new byte[srcPaletteSize];
+            if ((srcReds !is null) && (srcReds.length < srcPaletteSize)) srcPaletteSize = srcReds.length;
+            for (int i = 0, r, g, b, index; i < srcPaletteSize; ++i) {
+                r = srcReds[i] & 0xff;
+                g = srcGreens[i] & 0xff;
+                b = srcBlues[i] & 0xff;
+                index = 0;
+                int minDistance = 0x7fffffff;
+                for (int j = 0, dr, dg, db, distance; j < destPaletteSize; ++j) {
+                    dr = (destReds[j] & 0xff) - r;
+                    dg = (destGreens[j] & 0xff) - g;
+                    db = (destBlues[j] & 0xff) - b;
+                    distance = dr * dr + dg * dg + db * db;
+                    if (distance < minDistance) {
+                        index = j;
+                        if (distance is 0) break;
+                        minDistance = distance;
+                    }
+                }
+                paletteMapping[i] = cast(byte)index;
+                if (minDistance !is 0) isExactPaletteMapping = false;
+            }
+            break;
+        default:
+    }
+    if ((paletteMapping !is null) && (isExactPaletteMapping || ! ditherEnabled)) {
+        if ((stype is dtype) && (alphaMode is 0x10000)) {
+            /*** Fast blit (copy w/ mapping) ***/
+            switch (stype) {
+                case TYPE_INDEX_8:
+                    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                            destData[dp] = paletteMapping[srcData[sp] & 0xff];
+                            sp += (sfx >>> 16);
+                        }
+                    }
+                    break;
+                case TYPE_INDEX_4:
+                    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                            int v;
+                            if ((sp & 1) !is 0) v = paletteMapping[srcData[sp >> 1] & 0x0f];
+                            else v = (srcData[sp >> 1] >>> 4) & 0x0f;
+                            sp += (sfx >>> 16);
+                            if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | v);
+                            else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (v << 4));
+                        }
+                    }
+                    break;
+                case TYPE_INDEX_2:
+                    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                            int index = paletteMapping[(srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03];
+                            sp += (sfx >>> 16);
+                            int shift = 6 - (dp & 3) * 2;
+                            destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
+                        }
+                    }
+                    break;
+                case TYPE_INDEX_1_MSB:
+                    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                            int index = paletteMapping[(srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01];
+                            sp += (sfx >>> 16);
+                            int shift = 7 - (dp & 7);
+                            destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                        }
+                    }
+                    break;
+                case TYPE_INDEX_1_LSB:
+                    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
+                        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
+                            int index = paletteMapping[(srcData[sp >> 3] >>> (sp & 7)) & 0x01];
+                            sp += (sfx >>> 16);
+                            int shift = dp & 7;
+                            destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                        }
+                    }
+                    break;
+                default:
+            }
+        } else {
+            /*** Convert between indexed modes using mapping and mask ***/
+            for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+                    sp = spr += (sfy >>> 16) * srcStride,
+                    sfy = (sfy & 0xffff) + sfyi,
+                    dp = dpr += dpryi) {
+                for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                        dp += dprxi,
+                        sfx = (sfx & 0xffff) + sfxi) {
+                    int index;
+                    /*** READ NEXT PIXEL ***/
+                    switch (stype) {
+                        case TYPE_INDEX_8:
+                            index = srcData[sp] & 0xff;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_4:
+                            if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f;
+                            else index = (srcData[sp >> 1] >>> 4) & 0x0f;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_2:
+                            index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_1_MSB:
+                            index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
+                            sp += (sfx >>> 16);
+                            break;
+                        case TYPE_INDEX_1_LSB:
+                            index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
+                            sp += (sfx >>> 16);
+                            break;
+                        default:
+                            return;
+                    }
+                    /*** APPLY MASK ***/
+                    switch (alphaMode) {
+                        case ALPHA_MASK_UNPACKED: {
+                            byte mask = alphaData[ap];
+                            ap += (sfx >> 16);
+                            if (mask is 0) continue;
+                        } break;
+                        case ALPHA_MASK_PACKED: {
+                            int mask = alphaData[ap >> 3] & (1 << (ap & 7));
+                            ap += (sfx >> 16);
+                            if (mask is 0) continue;
+                        } break;
+                        case ALPHA_MASK_INDEX: {
+                            int i = 0;
+                            while (i < alphaData.length) {
+                                if (index is (alphaData[i] & 0xff)) break;
+                            }
+                            if (i < alphaData.length) continue;
+                        } break;
+                        case ALPHA_MASK_RGB: {
+                            byte r = srcReds[index], g = srcGreens[index], b = srcBlues[index];
+                            int i = 0;
+                            while (i < alphaData.length) {
+                                if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) break;
+                                i += 3;
+                            }
+                            if (i < alphaData.length) continue;
+                        } break;
+                        default:
+                    }
+                    index = paletteMapping[index] & 0xff;
+
+                    /*** WRITE NEXT PIXEL ***/
+                    switch (dtype) {
+                        case TYPE_INDEX_8:
+                            destData[dp] = cast(byte) index;
+                            break;
+                        case TYPE_INDEX_4:
+                            if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | index);
+                            else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (index << 4));
+                            break;
+                        case TYPE_INDEX_2: {
+                            int shift = 6 - (dp & 3) * 2;
+                            destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
+                        } break;
+                        case TYPE_INDEX_1_MSB: {
+                            int shift = 7 - (dp & 7);
+                            destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                        } break;
+                        case TYPE_INDEX_1_LSB: {
+                            int shift = dp & 7;
+                            destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
+                        } break;
+                        default:
+                    }
+                }
+            }
+        }
+        return;
+    }
+
+    /*** Comprehensive blit (apply transformations) ***/
+    int alpha = alphaMode;
+    int index = 0;
+    int indexq = 0;
+    int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
+    int[] rerr, gerr, berr;
+    if (ditherEnabled) {
+        rerr = new int[destWidth + 2];
+        gerr = new int[destWidth + 2];
+        berr = new int[destWidth + 2];
+    } else {
+        rerr = null; gerr = null; berr = null;
+    }
+    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+            sp = spr += (sfy >>> 16) * srcStride,
+            ap = apr += (sfy >>> 16) * alphaStride,
+            sfy = (sfy & 0xffff) + sfyi,
+            dp = dpr += dpryi) {
+        int lrerr = 0, lgerr = 0, lberr = 0;
+        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                dp += dprxi,
+                sfx = (sfx & 0xffff) + sfxi) {
+            /*** READ NEXT PIXEL ***/
+            switch (stype) {
+                case TYPE_INDEX_8:
+                    index = srcData[sp] & 0xff;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_4:
+                    if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f;
+                    else index = (srcData[sp >> 1] >>> 4) & 0x0f;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_2:
+                    index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_1_MSB:
+                    index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_1_LSB:
+                    index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
+                    sp += (sfx >>> 16);
+                    break;
+                default:
+            }
+
+            /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+            int r = srcReds[index] & 0xff, g = srcGreens[index] & 0xff, b = srcBlues[index] & 0xff;
+            switch (alphaMode) {
+                case ALPHA_CHANNEL_SEPARATE:
+                    alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_UNPACKED:
+                    alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_PACKED:
+                    alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
+                    int i = 0;
+                    while (i < alphaData.length) {
+                        if (index is (alphaData[i] & 0xff)) break;
+                    }
+                    if (i < alphaData.length) continue;
+                } break;
+                case ALPHA_MASK_RGB: {
+                    int i = 0;
+                    while (i < alphaData.length) {
+                        if ((r is (alphaData[i] & 0xff)) &&
+                            (g is (alphaData[i + 1] & 0xff)) &&
+                            (b is (alphaData[i + 2] & 0xff))) break;
+                        i += 3;
+                    }
+                    if (i < alphaData.length) continue;
+                } break;
+                default:
+            }
+            if (alpha !is 0x10000) {
+                if (alpha is 0x0000) continue;
+                switch (dtype) {
+                    case TYPE_INDEX_8:
+                        indexq = destData[dp] & 0xff;
+                        break;
+                    case TYPE_INDEX_4:
+                        if ((dp & 1) !is 0) indexq = destData[dp >> 1] & 0x0f;
+                        else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
+                        break;
+                    case TYPE_INDEX_2:
+                        indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
+                        break;
+                    case TYPE_INDEX_1_MSB:
+                        indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
+                        break;
+                    case TYPE_INDEX_1_LSB:
+                        indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
+                        break;
+                    default:
+                }
+                // Perform alpha blending
+                int rq = destReds[indexq] & 0xff;
+                int gq = destGreens[indexq] & 0xff;
+                int bq = destBlues[indexq] & 0xff;
+                r = rq + ((r - rq) * alpha >> 16);
+                g = gq + ((g - gq) * alpha >> 16);
+                b = bq + ((b - bq) * alpha >> 16);
+            }
+
+            /*** MAP COLOR TO THE PALETTE ***/
+            if (ditherEnabled) {
+                // Floyd-Steinberg error diffusion
+                r += rerr[dx] >> 4;
+                if (r < 0) r = 0; else if (r > 255) r = 255;
+                g += gerr[dx] >> 4;
+                if (g < 0) g = 0; else if (g > 255) g = 255;
+                b += berr[dx] >> 4;
+                if (b < 0) b = 0; else if (b > 255) b = 255;
+                rerr[dx] = lrerr;
+                gerr[dx] = lgerr;
+                berr[dx] = lberr;
+            }
+            if (r !is lastr || g !is lastg || b !is lastb) {
+                // moving the variable declarations out seems to make the JDK JIT happier...
+                for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
+                    dr = (destReds[j] & 0xff) - r;
+                    dg = (destGreens[j] & 0xff) - g;
+                    db = (destBlues[j] & 0xff) - b;
+                    distance = dr * dr + dg * dg + db * db;
+                    if (distance < minDistance) {
+                        lastindex = j;
+                        if (distance is 0) break;
+                        minDistance = distance;
+                    }
+                }
+                lastr = r; lastg = g; lastb = b;
+            }
+            if (ditherEnabled) {
+                // Floyd-Steinberg error diffusion, cont'd...
+                int dxm1 = dx - 1, dxp1 = dx + 1;
+                int acc;
+                rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
+                rerr[dx] += acc += lrerr + lrerr;
+                rerr[dxm1] += acc + lrerr + lrerr;
+                gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
+                gerr[dx] += acc += lgerr + lgerr;
+                gerr[dxm1] += acc + lgerr + lgerr;
+                berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
+                berr[dx] += acc += lberr + lberr;
+                berr[dxm1] += acc + lberr + lberr;
+            }
+
+            /*** WRITE NEXT PIXEL ***/
+            switch (dtype) {
+                case TYPE_INDEX_8:
+                    destData[dp] = cast(byte) lastindex;
+                    break;
+                case TYPE_INDEX_4:
+                    if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | lastindex);
+                    else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
+                    break;
+                case TYPE_INDEX_2: {
+                    int shift = 6 - (dp & 3) * 2;
+                    destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
+                } break;
+                case TYPE_INDEX_1_MSB: {
+                    int shift = 7 - (dp & 7);
+                    destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                } break;
+                case TYPE_INDEX_1_LSB: {
+                    int shift = dp & 7;
+                    destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                } break;
+                default:
+            }
+        }
+    }
+}
+
+/**
+ * Blits an index palette image into a direct palette image.
+ * <p>
+ * Note: The source and destination masks and palettes must
+ * always be fully specified.
+ * </p>
+ *
+ * @param op the blitter operation: a combination of BLIT_xxx flags
+ *        (see BLIT_xxx constants)
+ * @param srcData the source byte array containing image data
+ * @param srcDepth the source depth: one of 1, 2, 4, 8
+ * @param srcStride the source number of bytes per line
+ * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if srcDepth is not 1
+ * @param srcX the top-left x-coord of the source blit region
+ * @param srcY the top-left y-coord of the source blit region
+ * @param srcWidth the width of the source blit region
+ * @param srcHeight the height of the source blit region
+ * @param srcReds the source palette red component intensities
+ * @param srcGreens the source palette green component intensities
+ * @param srcBlues the source palette blue component intensities
+ * @param alphaMode the alpha blending or mask mode, may be
+ *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+ *        not specified in the blitter operations
+ *        (see ALPHA_MODE_xxx constants)
+ * @param alphaData the alpha blending or mask data, varies depending
+ *        on the value of alphaMode and sometimes ignored
+ * @param alphaStride the alpha data number of bytes per line
+ * @param alphaX the top-left x-coord of the alpha blit region
+ * @param alphaY the top-left y-coord of the alpha blit region
+ * @param destData the destination byte array containing image data
+ * @param destDepth the destination depth: one of 8, 16, 24, 32
+ * @param destStride the destination number of bytes per line
+ * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if destDepth is not 16 or 32
+ * @param destX the top-left x-coord of the destination blit region
+ * @param destY the top-left y-coord of the destination blit region
+ * @param destWidth the width of the destination blit region
+ * @param destHeight the height of the destination blit region
+ * @param destRedMask the destination red channel mask
+ * @param destGreenMask the destination green channel mask
+ * @param destBlueMask the destination blue channel mask
+ * @param flipX if true the resulting image is flipped along the vertical axis
+ * @param flipY if true the resulting image is flipped along the horizontal axis
+ */
+static void blit(int op,
+    byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+    int srcX, int srcY, int srcWidth, int srcHeight,
+    byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
+    int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+    byte[] destData, int destDepth, int destStride, int destOrder,
+    int destX, int destY, int destWidth, int destHeight,
+    int destRedMask, int destGreenMask, int destBlueMask,
+    bool flipX, bool flipY) {
+
+    static_this();
+
+    if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
+
+    // these should be supplied as params later
+    int destAlphaMask = 0;
+
+    /*** Prepare scaling data ***/
+    int dwm1 = destWidth - 1;
+    int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0;
+    int dhm1 = destHeight - 1;
+    int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0;
+
+    /*** Prepare source-related data ***/
+    int stype;
+    switch (srcDepth) {
+        case 8:
+            stype = TYPE_INDEX_8;
+            break;
+        case 4:
+            srcStride <<= 1;
+            stype = TYPE_INDEX_4;
+            break;
+        case 2:
+            srcStride <<= 2;
+            stype = TYPE_INDEX_2;
+            break;
+        case 1:
+            srcStride <<= 3;
+            stype = (srcOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid source type");
+            return;
+    }
+    int spr = srcY * srcStride + srcX;
+
+    /*** Prepare destination-related data ***/
+    int dbpp, dtype;
+    switch (destDepth) {
+        case 8:
+            dbpp = 1;
+            dtype = TYPE_GENERIC_8;
+            break;
+        case 16:
+            dbpp = 2;
+            dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+            break;
+        case 24:
+            dbpp = 3;
+            dtype = TYPE_GENERIC_24;
+            break;
+        case 32:
+            dbpp = 4;
+            dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid destination type");
+            return;
+    }
+    int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
+    int dprxi = (flipX) ? -dbpp : dbpp;
+    int dpryi = (flipY) ? -destStride : destStride;
+
+    /*** Prepare special processing data ***/
+    int apr;
+    if ((op & BLIT_ALPHA) !is 0) {
+        switch (alphaMode) {
+            case ALPHA_MASK_UNPACKED:
+            case ALPHA_CHANNEL_SEPARATE:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_PACKED:
+                if (alphaData is null) alphaMode = 0x10000;
+                alphaStride <<= 3;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_INDEX:
+            case ALPHA_MASK_RGB:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = 0;
+                break;
+            default:
+                alphaMode = (alphaMode << 16) / 255; // prescale
+            case ALPHA_CHANNEL_SOURCE:
+                apr = 0;
+                break;
+        }
+    } else {
+        alphaMode = 0x10000;
+        apr = 0;
+    }
+
+    /*** Comprehensive blit (apply transformations) ***/
+    int destRedShift = getChannelShift(destRedMask);
+    int destRedWidth = getChannelWidth(destRedMask, destRedShift);
+    byte[] destReds = ANY_TO_EIGHT[destRedWidth];
+    int destRedPreShift = 8 - destRedWidth;
+    int destGreenShift = getChannelShift(destGreenMask);
+    int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
+    byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
+    int destGreenPreShift = 8 - destGreenWidth;
+    int destBlueShift = getChannelShift(destBlueMask);
+    int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
+    byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
+    int destBluePreShift = 8 - destBlueWidth;
+    int destAlphaShift = getChannelShift(destAlphaMask);
+    int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
+    byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
+    int destAlphaPreShift = 8 - destAlphaWidth;
+
+    int dp = dpr;
+    int sp = spr;
+    int ap = apr, alpha = alphaMode;
+    int r = 0, g = 0, b = 0, a = 0, index = 0;
+    int rq = 0, gq = 0, bq = 0, aq = 0;
+    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+            sp = spr += (sfy >>> 16) * srcStride,
+            ap = apr += (sfy >>> 16) * alphaStride,
+            sfy = (sfy & 0xffff) + sfyi,
+            dp = dpr += dpryi) {
+        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                dp += dprxi,
+                sfx = (sfx & 0xffff) + sfxi) {
+            /*** READ NEXT PIXEL ***/
+            switch (stype) {
+                case TYPE_INDEX_8:
+                    index = srcData[sp] & 0xff;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_4:
+                    if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f;
+                    else index = (srcData[sp >> 1] >>> 4) & 0x0f;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_2:
+                    index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_1_MSB:
+                    index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
+                    sp += (sfx >>> 16);
+                    break;
+                case TYPE_INDEX_1_LSB:
+                    index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
+                    sp += (sfx >>> 16);
+                    break;
+                default:
+            }
+
+            /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+            r = srcReds[index] & 0xff;
+            g = srcGreens[index] & 0xff;
+            b = srcBlues[index] & 0xff;
+            switch (alphaMode) {
+                case ALPHA_CHANNEL_SEPARATE:
+                    alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_UNPACKED:
+                    alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_PACKED:
+                    alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
+                    int i = 0;
+                    while (i < alphaData.length) {
+                        if (index is (alphaData[i] & 0xff)) break;
+                    }
+                    if (i < alphaData.length) continue;
+                } break;
+                case ALPHA_MASK_RGB: {
+                    int i = 0;
+                    while (i < alphaData.length) {
+                        if ((r is (alphaData[i] & 0xff)) &&
+                            (g is (alphaData[i + 1] & 0xff)) &&
+                            (b is (alphaData[i + 2] & 0xff))) break;
+                        i += 3;
+                    }
+                    if (i < alphaData.length) continue;
+                } break;
+                default:
+            }
+            if (alpha !is 0x10000) {
+                if (alpha is 0x0000) continue;
+                switch (dtype) {
+                    case TYPE_GENERIC_8: {
+                        int data = destData[dp] & 0xff;
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_16_MSB: {
+                        int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_16_LSB: {
+                        int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_24: {
+                        int data = (( ((destData[dp] & 0xff) << 8) |
+                            (destData[dp + 1] & 0xff)) << 8) |
+                            (destData[dp + 2] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_32_MSB: {
+                        int data = (( (( ((destData[dp] & 0xff) << 8) |
+                            (destData[dp + 1] & 0xff)) << 8) |
+                            (destData[dp + 2] & 0xff)) << 8) |
+                            (destData[dp + 3] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    case TYPE_GENERIC_32_LSB: {
+                        int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
+                            (destData[dp + 2] & 0xff)) << 8) |
+                            (destData[dp + 1] & 0xff)) << 8) |
+                            (destData[dp] & 0xff);
+                        rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
+                        gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
+                        bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
+                        aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
+                    } break;
+                    default:
+                }
+                // Perform alpha blending
+                a = aq + ((a - aq) * alpha >> 16);
+                r = rq + ((r - rq) * alpha >> 16);
+                g = gq + ((g - gq) * alpha >> 16);
+                b = bq + ((b - bq) * alpha >> 16);
+            }
+
+            /*** WRITE NEXT PIXEL ***/
+            int data =
+                (r >>> destRedPreShift << destRedShift) |
+                (g >>> destGreenPreShift << destGreenShift) |
+                (b >>> destBluePreShift << destBlueShift) |
+                (a >>> destAlphaPreShift << destAlphaShift);
+            switch (dtype) {
+                case TYPE_GENERIC_8: {
+                    destData[dp] = cast(byte) data;
+                } break;
+                case TYPE_GENERIC_16_MSB: {
+                    destData[dp] = cast(byte) (data >>> 8);
+                    destData[dp + 1] = cast(byte) (data & 0xff);
+                } break;
+                case TYPE_GENERIC_16_LSB: {
+                    destData[dp] = cast(byte) (data & 0xff);
+                    destData[dp + 1] = cast(byte) (data >>> 8);
+                } break;
+                case TYPE_GENERIC_24: {
+                    destData[dp] = cast(byte) (data >>> 16);
+                    destData[dp + 1] = cast(byte) (data >>> 8);
+                    destData[dp + 2] = cast(byte) (data & 0xff);
+                } break;
+                case TYPE_GENERIC_32_MSB: {
+                    destData[dp] = cast(byte) (data >>> 24);
+                    destData[dp + 1] = cast(byte) (data >>> 16);
+                    destData[dp + 2] = cast(byte) (data >>> 8);
+                    destData[dp + 3] = cast(byte) (data & 0xff);
+                } break;
+                case TYPE_GENERIC_32_LSB: {
+                    destData[dp] = cast(byte) (data & 0xff);
+                    destData[dp + 1] = cast(byte) (data >>> 8);
+                    destData[dp + 2] = cast(byte) (data >>> 16);
+                    destData[dp + 3] = cast(byte) (data >>> 24);
+                } break;
+                default:
+            }
+        }
+    }
+}
+
+/**
+ * Blits a direct palette image into an index palette image.
+ * <p>
+ * Note: The source and destination masks and palettes must
+ * always be fully specified.
+ * </p>
+ *
+ * @param op the blitter operation: a combination of BLIT_xxx flags
+ *        (see BLIT_xxx constants)
+ * @param srcData the source byte array containing image data
+ * @param srcDepth the source depth: one of 8, 16, 24, 32
+ * @param srcStride the source number of bytes per line
+ * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if srcDepth is not 16 or 32
+ * @param srcX the top-left x-coord of the source blit region
+ * @param srcY the top-left y-coord of the source blit region
+ * @param srcWidth the width of the source blit region
+ * @param srcHeight the height of the source blit region
+ * @param srcRedMask the source red channel mask
+ * @param srcGreenMask the source green channel mask
+ * @param srcBlueMask the source blue channel mask
+ * @param alphaMode the alpha blending or mask mode, may be
+ *        an integer 0-255 for global alpha; ignored if BLIT_ALPHA
+ *        not specified in the blitter operations
+ *        (see ALPHA_MODE_xxx constants)
+ * @param alphaData the alpha blending or mask data, varies depending
+ *        on the value of alphaMode and sometimes ignored
+ * @param alphaStride the alpha data number of bytes per line
+ * @param alphaX the top-left x-coord of the alpha blit region
+ * @param alphaY the top-left y-coord of the alpha blit region
+ * @param destData the destination byte array containing image data
+ * @param destDepth the destination depth: one of 1, 2, 4, 8
+ * @param destStride the destination number of bytes per line
+ * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
+ *        ignored if destDepth is not 1
+ * @param destX the top-left x-coord of the destination blit region
+ * @param destY the top-left y-coord of the destination blit region
+ * @param destWidth the width of the destination blit region
+ * @param destHeight the height of the destination blit region
+ * @param destReds the destination palette red component intensities
+ * @param destGreens the destination palette green component intensities
+ * @param destBlues the destination palette blue component intensities
+ * @param flipX if true the resulting image is flipped along the vertical axis
+ * @param flipY if true the resulting image is flipped along the horizontal axis
+ */
+static void blit(int op,
+    byte[] srcData, int srcDepth, int srcStride, int srcOrder,
+    int srcX, int srcY, int srcWidth, int srcHeight,
+    int srcRedMask, int srcGreenMask, int srcBlueMask,
+    int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
+    byte[] destData, int destDepth, int destStride, int destOrder,
+    int destX, int destY, int destWidth, int destHeight,
+    byte[] destReds, byte[] destGreens, byte[] destBlues,
+    bool flipX, bool flipY) {
+
+    static_this();
+
+    if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
+
+    // these should be supplied as params later
+    int srcAlphaMask = 0;
+
+    /*** Prepare scaling data ***/
+    int dwm1 = destWidth - 1;
+    int sfxi = (dwm1 !is 0) ? cast(int)(((cast(long)srcWidth << 16) - 1) / dwm1) : 0;
+    int dhm1 = destHeight - 1;
+    int sfyi = (dhm1 !is 0) ? cast(int)(((cast(long)srcHeight << 16) - 1) / dhm1) : 0;
+
+    /*** Prepare source-related data ***/
+    int sbpp, stype;
+    switch (srcDepth) {
+        case 8:
+            sbpp = 1;
+            stype = TYPE_GENERIC_8;
+            break;
+        case 16:
+            sbpp = 2;
+            stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
+            break;
+        case 24:
+            sbpp = 3;
+            stype = TYPE_GENERIC_24;
+            break;
+        case 32:
+            sbpp = 4;
+            stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid source type");
+            return;
+    }
+    int spr = srcY * srcStride + srcX * sbpp;
+
+    /*** Prepare destination-related data ***/
+    int dtype;
+    switch (destDepth) {
+        case 8:
+            dtype = TYPE_INDEX_8;
+            break;
+        case 4:
+            destStride <<= 1;
+            dtype = TYPE_INDEX_4;
+            break;
+        case 2:
+            destStride <<= 2;
+            dtype = TYPE_INDEX_2;
+            break;
+        case 1:
+            destStride <<= 3;
+            dtype = (destOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
+            break;
+        default:
+            //throw new IllegalArgumentException("Invalid source type");
+            return;
+    }
+    int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
+    int dprxi = (flipX) ? -1 : 1;
+    int dpryi = (flipY) ? -destStride : destStride;
+
+    /*** Prepare special processing data ***/
+    int apr;
+    if ((op & BLIT_ALPHA) !is 0) {
+        switch (alphaMode) {
+            case ALPHA_MASK_UNPACKED:
+            case ALPHA_CHANNEL_SEPARATE:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_PACKED:
+                if (alphaData is null) alphaMode = 0x10000;
+                alphaStride <<= 3;
+                apr = alphaY * alphaStride + alphaX;
+                break;
+            case ALPHA_MASK_INDEX:
+                //throw new IllegalArgumentException("Invalid alpha type");
+                return;
+            case ALPHA_MASK_RGB:
+                if (alphaData is null) alphaMode = 0x10000;
+                apr = 0;
+                break;
+            default:
+                alphaMode = (alphaMode << 16) / 255; // prescale
+            case ALPHA_CHANNEL_SOURCE:
+                apr = 0;
+                break;
+        }
+    } else {
+        alphaMode = 0x10000;
+        apr = 0;
+    }
+    bool ditherEnabled = (op & BLIT_DITHER) !is 0;
+
+    /*** Comprehensive blit (apply transformations) ***/
+    int srcRedShift = getChannelShift(srcRedMask);
+    byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
+    int srcGreenShift = getChannelShift(srcGreenMask);
+    byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
+    int srcBlueShift = getChannelShift(srcBlueMask);
+    byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
+    int srcAlphaShift = getChannelShift(srcAlphaMask);
+    byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
+
+    int dp = dpr;
+    int sp = spr;
+    int ap = apr, alpha = alphaMode;
+    int r = 0, g = 0, b = 0, a = 0;
+    int indexq = 0;
+    int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
+    int[] rerr, gerr, berr;
+    int destPaletteSize = 1 << destDepth;
+    if ((destReds !is null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
+    if (ditherEnabled) {
+        rerr = new int[destWidth + 2];
+        gerr = new int[destWidth + 2];
+        berr = new int[destWidth + 2];
+    } else {
+        rerr = null; gerr = null; berr = null;
+    }
+    for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
+            sp = spr += (sfy >>> 16) * srcStride,
+            ap = apr += (sfy >>> 16) * alphaStride,
+            sfy = (sfy & 0xffff) + sfyi,
+            dp = dpr += dpryi) {
+        int lrerr = 0, lgerr = 0, lberr = 0;
+        for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
+                dp += dprxi,
+                sfx = (sfx & 0xffff) + sfxi) {
+            /*** READ NEXT PIXEL ***/
+            switch (stype) {
+                case TYPE_GENERIC_8: {
+                    int data = srcData[sp] & 0xff;
+                    sp += (sfx >>> 16);
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_16_MSB: {
+                    int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
+                    sp += (sfx >>> 16) * 2;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_16_LSB: {
+                    int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
+                    sp += (sfx >>> 16) * 2;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_24: {
+                    int data = (( ((srcData[sp] & 0xff) << 8) |
+                        (srcData[sp + 1] & 0xff)) << 8) |
+                        (srcData[sp + 2] & 0xff);
+                    sp += (sfx >>> 16) * 3;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_32_MSB: {
+                    int data = (( (( ((srcData[sp] & 0xff) << 8) |
+                        (srcData[sp + 1] & 0xff)) << 8) |
+                        (srcData[sp + 2] & 0xff)) << 8) |
+                        (srcData[sp + 3] & 0xff);
+                    sp += (sfx >>> 16) * 4;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                case TYPE_GENERIC_32_LSB: {
+                    int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
+                        (srcData[sp + 2] & 0xff)) << 8) |
+                        (srcData[sp + 1] & 0xff)) << 8) |
+                        (srcData[sp] & 0xff);
+                    sp += (sfx >>> 16) * 4;
+                    r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
+                    g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
+                    b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
+                    a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
+                } break;
+                default:
+            }
+
+            /*** DO SPECIAL PROCESSING IF REQUIRED ***/
+            switch (alphaMode) {
+                case ALPHA_CHANNEL_SEPARATE:
+                    alpha = ((alphaData[ap] & 0xff) << 16) / 255;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_CHANNEL_SOURCE:
+                    alpha = (a << 16) / 255;
+                    break;
+                case ALPHA_MASK_UNPACKED:
+                    alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_PACKED:
+                    alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
+                    ap += (sfx >> 16);
+                    break;
+                case ALPHA_MASK_RGB:
+                    alpha = 0x10000;
+                    for (int i = 0; i < alphaData.length; i += 3) {
+                        if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) {
+                            alpha = 0x0000;
+                            break;
+                        }
+                    }
+                    break;
+                default:
+            }
+            if (alpha !is 0x10000) {
+                if (alpha is 0x0000) continue;
+                switch (dtype) {
+                    case TYPE_INDEX_8:
+                        indexq = destData[dp] & 0xff;
+                        break;
+                    case TYPE_INDEX_4:
+                        if ((dp & 1) !is 0) indexq = destData[dp >> 1] & 0x0f;
+                        else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
+                        break;
+                    case TYPE_INDEX_2:
+                        indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
+                        break;
+                    case TYPE_INDEX_1_MSB:
+                        indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
+                        break;
+                    case TYPE_INDEX_1_LSB:
+                        indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
+                        break;
+                    default:
+                }
+                // Perform alpha blending
+                int rq = destReds[indexq] & 0xff;
+                int gq = destGreens[indexq] & 0xff;
+                int bq = destBlues[indexq] & 0xff;
+                r = rq + ((r - rq) * alpha >> 16);
+                g = gq + ((g - gq) * alpha >> 16);
+                b = bq + ((b - bq) * alpha >> 16);
+            }
+
+            /*** MAP COLOR TO THE PALETTE ***/
+            if (ditherEnabled) {
+                // Floyd-Steinberg error diffusion
+                r += rerr[dx] >> 4;
+                if (r < 0) r = 0; else if (r > 255) r = 255;
+                g += gerr[dx] >> 4;
+                if (g < 0) g = 0; else if (g > 255) g = 255;
+                b += berr[dx] >> 4;
+                if (b < 0) b = 0; else if (b > 255) b = 255;
+                rerr[dx] = lrerr;
+                gerr[dx] = lgerr;
+                berr[dx] = lberr;
+            }
+            if (r !is lastr || g !is lastg || b !is lastb) {
+                // moving the variable declarations out seems to make the JDK JIT happier...
+                for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
+                    dr = (destReds[j] & 0xff) - r;
+                    dg = (destGreens[j] & 0xff) - g;
+                    db = (destBlues[j] & 0xff) - b;
+                    distance = dr * dr + dg * dg + db * db;
+                    if (distance < minDistance) {
+                        lastindex = j;
+                        if (distance is 0) break;
+                        minDistance = distance;
+                    }
+                }
+                lastr = r; lastg = g; lastb = b;
+            }
+            if (ditherEnabled) {
+                // Floyd-Steinberg error diffusion, cont'd...
+                int dxm1 = dx - 1, dxp1 = dx + 1;
+                int acc;
+                rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
+                rerr[dx] += acc += lrerr + lrerr;
+                rerr[dxm1] += acc + lrerr + lrerr;
+                gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
+                gerr[dx] += acc += lgerr + lgerr;
+                gerr[dxm1] += acc + lgerr + lgerr;
+                berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
+                berr[dx] += acc += lberr + lberr;
+                berr[dxm1] += acc + lberr + lberr;
+            }
+
+            /*** WRITE NEXT PIXEL ***/
+            switch (dtype) {
+                case TYPE_INDEX_8:
+                    destData[dp] = cast(byte) lastindex;
+                    break;
+                case TYPE_INDEX_4:
+                    if ((dp & 1) !is 0) destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0xf0) | lastindex);
+                    else destData[dp >> 1] = cast(byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
+                    break;
+                case TYPE_INDEX_2: {
+                    int shift = 6 - (dp & 3) * 2;
+                    destData[dp >> 2] = cast(byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
+                } break;
+                case TYPE_INDEX_1_MSB: {
+                    int shift = 7 - (dp & 7);
+                    destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                } break;
+                case TYPE_INDEX_1_LSB: {
+                    int shift = dp & 7;
+                    destData[dp >> 3] = cast(byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
+                } break;
+                default:
+            }
+        }
+    }
+}
+
+/**
+ * Computes the required channel shift from a mask.
+ */
+static int getChannelShift(int mask) {
+    if (mask is 0) return 0;
+    int i;
+    for (i = 0; ((mask & 1) is 0) && (i < 32); ++i) {
+        mask >>>= 1;
+    }
+    return i;
+}
+
+/**
+ * Computes the required channel width (depth) from a mask.
+ */
+static int getChannelWidth(int mask, int shift) {
+    if (mask is 0) return 0;
+    int i;
+    mask >>>= shift;
+    for (i = shift; ((mask & 1) !is 0) && (i < 32); ++i) {
+        mask >>>= 1;
+    }
+    return i - shift;
+}
+
+/**
+ * Extracts a field from packed RGB data given a mask for that field.
+ */
+static byte getChannelField(int data, int mask) {
+    static_this();
+    int shift = getChannelShift(mask);
+    return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift];
+}
+
+/**
+ * Creates an ImageData containing one band's worth of a gradient filled
+ * block.  If <code>vertical</code> is true, the band must be tiled
+ * horizontally to fill a region, otherwise it must be tiled vertically.
+ *
+ * @param width the width of the region to be filled
+ * @param height the height of the region to be filled
+ * @param vertical if true sweeps from top to bottom, else
+ *        sweeps from left to right
+ * @param fromRGB the color to start with
+ * @param toRGB the color to end with
+ * @param redBits the number of significant red bits, 0 for palette modes
+ * @param greenBits the number of significant green bits, 0 for palette modes
+ * @param blueBits the number of significant blue bits, 0 for palette modes
+ * @return the new ImageData
+ */
+static ImageData createGradientBand(
+    int width, int height, bool vertical,
+    RGB fromRGB, RGB toRGB,
+    int redBits, int greenBits, int blueBits) {
+    /* Gradients are drawn as tiled bands */
+    int bandWidth, bandHeight, bitmapDepth;
+    byte[] bitmapData;
+    PaletteData paletteData;
+    /* Select an algorithm depending on the depth of the screen */
+    if (redBits !is 0 && greenBits !is 0 && blueBits !is 0) {
+        paletteData = new PaletteData(0x0000ff00, 0x00ff0000, 0xff000000);
+        bitmapDepth = 32;
+        if (redBits >= 8 && greenBits >= 8 && blueBits >= 8) {
+            /* Precise color */
+            int steps;
+            if (vertical) {
+                bandWidth = 1;
+                bandHeight = height;
+                steps = bandHeight > 1 ? bandHeight - 1 : 1;
+            } else {
+                bandWidth = width;
+                bandHeight = 1;
+                steps = bandWidth > 1 ? bandWidth - 1 : 1;
+            }
+            int bytesPerLine = bandWidth * 4;
+            bitmapData = new byte[bandHeight * bytesPerLine];
+            buildPreciseGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine);
+            buildPreciseGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine);
+            buildPreciseGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine);
+        } else {
+            /* Dithered color */
+            int steps;
+            if (vertical) {
+                bandWidth = (width < 8) ? width : 8;
+                bandHeight = height;
+                steps = bandHeight > 1 ? bandHeight - 1 : 1;
+            } else {
+                bandWidth = width;
+                bandHeight = (height < 8) ? height : 8;
+                steps = bandWidth > 1 ? bandWidth - 1 : 1;
+            }
+            int bytesPerLine = bandWidth * 4;
+            bitmapData = new byte[bandHeight * bytesPerLine];
+            buildDitheredGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine, blueBits);
+            buildDitheredGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine, greenBits);
+            buildDitheredGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine, redBits);
+        }
+    } else {
+        /* Dithered two tone */
+        paletteData = new PaletteData([ fromRGB, toRGB ]);
+        bitmapDepth = 8;
+        int blendi;
+        if (vertical) {
+            bandWidth = (width < 8) ? width : 8;
+            bandHeight = height;
+            blendi = (bandHeight > 1) ? 0x1040000 / (bandHeight - 1) + 1 : 1;
+        } else {
+            bandWidth = width;
+            bandHeight = (height < 8) ? height : 8;
+            blendi = (bandWidth > 1) ? 0x1040000 / (bandWidth - 1) + 1 : 1;
+        }
+        int bytesPerLine = (bandWidth + 3) & -4;
+        bitmapData = new byte[bandHeight * bytesPerLine];
+        if (vertical) {
+            for (int dy = 0, blend = 0, dp = 0; dy < bandHeight;
+                ++dy, blend += blendi, dp += bytesPerLine) {
+                for (int dx = 0; dx < bandWidth; ++dx) {
+                    bitmapData[dp + dx] = (blend + DITHER_MATRIX[dy & 7][dx]) <
+                        0x1000000 ? cast(byte)0 : cast(byte)1;
+                }
+            }
+        } else {
+            for (int dx = 0, blend = 0; dx < bandWidth; ++dx, blend += blendi) {
+                for (int dy = 0, dptr = dx; dy < bandHeight; ++dy, dptr += bytesPerLine) {
+                    bitmapData[dptr] = (blend + DITHER_MATRIX[dy][dx & 7]) <
+                        0x1000000 ? cast(byte)0 : cast(byte)1;
+                }
+            }
+        }
+    }
+    return new ImageData(bandWidth, bandHeight, bitmapDepth, paletteData, 4, bitmapData);
+}
+
+/*
+ * Fill in gradated values for a color channel
+ */
+static final void buildPreciseGradientChannel(int from, int to, int steps,
+    int bandWidth, int bandHeight, bool vertical,
+    byte[] bitmapData, int dp, int bytesPerLine) {
+    int val = from << 16;
+    int inc = ((to << 16) - val) / steps + 1;
+    if (vertical) {
+        for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
+            bitmapData[dp] = cast(byte)(val >>> 16);
+            val += inc;
+        }
+    } else {
+        for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
+            bitmapData[dp] = cast(byte)(val >>> 16);
+            val += inc;
+        }
+    }
+}
+
+/*
+ * Fill in dithered gradated values for a color channel
+ */
+static final void buildDitheredGradientChannel(int from, int to, int steps,
+    int bandWidth, int bandHeight, bool vertical,
+    byte[] bitmapData, int dp, int bytesPerLine, int bits) {
+    int mask = 0xff00 >>> bits;
+    int val = from << 16;
+    int inc = ((to << 16) - val) / steps + 1;
+    if (vertical) {
+        for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
+            for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) {
+                int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits;
+                int temp = val + thresh;
+                if (temp > 0xffffff) bitmapData[dptr] = -1;
+                else bitmapData[dptr] = cast(byte)((temp >>> 16) & mask);
+            }
+            val += inc;
+        }
+    } else {
+        for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
+            for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) {
+                int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits;
+                int temp = val + thresh;
+                if (temp > 0xffffff) bitmapData[dptr] = -1;
+                else bitmapData[dptr] = cast(byte)((temp >>> 16) & mask);
+            }
+            val += inc;
+        }
+    }
+}
+
+/**
+ * Renders a gradient onto a GC.
+ * <p>
+ * This is a GC helper.
+ * </p>
+ *
+ * @param gc the GC to render the gradient onto
+ * @param device the device the GC belongs to
+ * @param x the top-left x coordinate of the region to be filled
+ * @param y the top-left y coordinate of the region to be filled
+ * @param width the width of the region to be filled
+ * @param height the height of the region to be filled
+ * @param vertical if true sweeps from top to bottom, else
+ *        sweeps from left to right
+ * @param fromRGB the color to start with
+ * @param toRGB the color to end with
+ * @param redBits the number of significant red bits, 0 for palette modes
+ * @param greenBits the number of significant green bits, 0 for palette modes
+ * @param blueBits the number of significant blue bits, 0 for palette modes
+ */
+static void fillGradientRectangle(GC gc, Device device,
+    int x, int y, int width, int height, bool vertical,
+    RGB fromRGB, RGB toRGB,
+    int redBits, int greenBits, int blueBits) {
+    /* Create the bitmap and tile it */
+    ImageData band = createGradientBand(width, height, vertical,
+        fromRGB, toRGB, redBits, greenBits, blueBits);
+    Image image = new Image(device, band);
+    if ((band.width is 1) || (band.height is 1)) {
+        gc.drawImage(image, 0, 0, band.width, band.height, x, y, width, height);
+    } else {
+        if (vertical) {
+            for (int dx = 0; dx < width; dx += band.width) {
+                int blitWidth = width - dx;
+                if (blitWidth > band.width) blitWidth = band.width;
+                gc.drawImage(image, 0, 0, blitWidth, band.height, dx + x, y, blitWidth, band.height);
+            }
+        } else {
+            for (int dy = 0; dy < height; dy += band.height) {
+                int blitHeight = height - dy;
+                if (blitHeight > band.height) blitHeight = band.height;
+                gc.drawImage(image, 0, 0, band.width, blitHeight, x, dy + y, band.width, blitHeight);
+            }
+        }
+    }
+    image.dispose();
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageDataLoader.d	Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.graphics.ImageDataLoader;
+
+public import dwt.graphics.ImageData;
+public import dwt.dwthelper.InputStream;
+
+import dwt.graphics.ImageLoader;
+
+/**
+ * Internal class that separates ImageData from ImageLoader
+ * to allow removal of ImageLoader from the toolkit.
+ */
+class ImageDataLoader {
+
+    public static ImageData[] load(InputStream stream) {
+        return (new ImageLoader()).load(stream);
+    }
+
+    public static ImageData[] load(char[] filename) {
+        return (new ImageLoader()).load(filename);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageLoader.d	Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.graphics.ImageLoader;
+
+
+public import dwt.graphics.ImageLoaderListener;
+public import dwt.graphics.ImageLoaderEvent;
+public import dwt.graphics.ImageData;
+
+import dwt.DWT;
+import dwt.internal.Compatibility;
+import dwt.internal.image.FileFormat;
+
+import tango.core.Exception;
+import tango.core.Array;
+
+
+/**
+ * Instances of this class are used to load images from,
+ * and save images to, a file or stream.
+ * <p>
+ * Currently supported image formats are:
+ * </p><ul>
+ * <li>BMP (Windows or OS/2 Bitmap)</li>
+ * <li>ICO (Windows Icon)</li>
+ * <li>JPEG</li>
+ * <li>GIF</li>
+ * <li>PNG</li>
+ * <li>TIFF</li>
+ * </ul>
+ * <code>ImageLoaders</code> can be used to:
+ * <ul>
+ * <li>load/save single images in all formats</li>
+ * <li>load/save multiple images (GIF/ICO/TIFF)</li>
+ * <li>load/save animated GIF images</li>
+ * <li>load interlaced GIF/PNG images</li>
+ * <li>load progressive JPEG images</li>
+ * </ul>
+ */
+
+public class ImageLoader {
+
+    /**
+     * the array of ImageData objects in this ImageLoader.
+     * This array is read in when the load method is called,
+     * and it is written out when the save method is called
+     */
+    public ImageData[] data;
+
+    /**
+     * the width of the logical screen on which the images
+     * reside, in pixels (this corresponds to the GIF89a
+     * Logical Screen Width value)
+     */
+    public int logicalScreenWidth;
+
+    /**
+     * the height of the logical screen on which the images
+     * reside, in pixels (this corresponds to the GIF89a
+     * Logical Screen Height value)
+     */
+    public int logicalScreenHeight;
+
+    /**
+     * the background pixel for the logical screen (this
+     * corresponds to the GIF89a Background Color Index value).
+     * The default is -1 which means 'unspecified background'
+     *
+     */
+    public int backgroundPixel;
+
+    /**
+     * the number of times to repeat the display of a sequence
+     * of animated images (this corresponds to the commonly-used
+     * GIF application extension for "NETSCAPE 2.0 01").
+     * The default is 1. A value of 0 means 'display repeatedly'
+     */
+    public int repeatCount;
+
+    /*
+     * the set of ImageLoader event listeners, created on demand
+     */
+    ImageLoaderListener[] imageLoaderListeners;
+
+/**
+ * Construct a new empty ImageLoader.
+ */
+public this() {
+    reset();
+}
+
+/**
+ * Resets the fields of the ImageLoader, except for the
+ * <code>imageLoaderListeners</code> field.
+ */
+void reset() {
+    data = null;
+    logicalScreenWidth = 0;
+    logicalScreenHeight = 0;
+    backgroundPixel = -1;
+    repeatCount = 1;
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. Throws an error if either an error
+ * occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ */
+public ImageData[] load(InputStream stream) {
+    if (stream is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    reset();
+    data = FileFormat.load(stream, this);
+    return data;
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * file with the specified name. Throws an error if either
+ * an error occurs while loading the images, or if the images are
+ * not of a supported type. Returns the loaded image data array.
+ *
+ * @param filename the name of the file to load the images from
+ * @return an array of <code>ImageData</code> objects loaded from the specified file
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while reading from the file</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+ * </ul>
+ */
+public ImageData[] load(char[] filename) {
+    if (filename is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    InputStream stream = null;
+    void close(){
+        try {
+            if( stream !is null ) stream.close();
+        } catch (IOException e) {
+            // Ignore error
+        }
+    }
+    try {
+        stream = Compatibility.newFileInputStream(filename);
+        scope(exit) close();
+
+        return load(stream);
+    } catch (IOException e) {
+        DWT.error(DWT.ERROR_IO, e);
+    }
+    return null;
+}
+
+/**
+ * Saves the image data in this ImageLoader to the specified stream.
+ * The format parameter can have one of the following values:
+ * <dl>
+ * <dt><code>IMAGE_BMP</code></dt>
+ * <dd>Windows BMP file format, no compression</dd>
+ * <dt><code>IMAGE_BMP_RLE</code></dt>
+ * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+ * <dt><code>IMAGE_GIF</code></dt>
+ * <dd>GIF file format</dd>
+ * <dt><code>IMAGE_ICO</code></dt>
+ * <dd>Windows ICO file format</dd>
+ * <dt><code>IMAGE_JPEG</code></dt>
+ * <dd>JPEG file format</dd>
+ * <dt><code>IMAGE_PNG</code></dt>
+ * <dd>PNG file format</dd>
+ * </dl>
+ *
+ * @param stream the output stream to write the images to
+ * @param format the format to write the images in
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while writing to the stream</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image data contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image data cannot be saved to the requested format</li>
+ * </ul>
+ */
+public void save(OutputStream stream, int format) {
+    if (stream is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    FileFormat.save(stream, format, this);
+}
+
+/**
+ * Saves the image data in this ImageLoader to a file with the specified name.
+ * The format parameter can have one of the following values:
+ * <dl>
+ * <dt><code>IMAGE_BMP</code></dt>
+ * <dd>Windows BMP file format, no compression</dd>
+ * <dt><code>IMAGE_BMP_RLE</code></dt>
+ * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+ * <dt><code>IMAGE_GIF</code></dt>
+ * <dd>GIF file format</dd>
+ * <dt><code>IMAGE_ICO</code></dt>
+ * <dd>Windows ICO file format</dd>
+ * <dt><code>IMAGE_JPEG</code></dt>
+ * <dd>JPEG file format</dd>
+ * <dt><code>IMAGE_PNG</code></dt>
+ * <dd>PNG file format</dd>
+ * </dl>
+ *
+ * @param filename the name of the file to write the images to
+ * @param format the format to write the images in
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_IO - if an IO error occurs while writing to the file</li>
+ *    <li>ERROR_INVALID_IMAGE - if the image data contains invalid data</li>
+ *    <li>ERROR_UNSUPPORTED_FORMAT - if the image data cannot be saved to the requested format</li>
+ * </ul>
+ */
+public void save(char[] filename, int format) {
+    if (filename is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    OutputStream stream = null;
+    try {
+        stream = Compatibility.newFileOutputStream(filename);
+    } catch (IOException e) {
+        DWT.error(DWT.ERROR_IO, e);
+    }
+    save(stream, format);
+    try {
+        stream.close();
+    } catch (IOException e) {
+    }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will be
+ * notified when image data is either partially or completely loaded.
+ * <p>
+ * An ImageLoaderListener should be added before invoking
+ * one of the receiver's load methods. The listener's
+ * <code>imageDataLoaded</code> method is called when image
+ * data has been partially loaded, as is supported by interlaced
+ * GIF/PNG or progressive JPEG images.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see ImageLoaderListener
+ * @see ImageLoaderEvent
+ */
+public void addImageLoaderListener(ImageLoaderListener listener) {
+    if (listener is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    imageLoaderListeners ~= listener;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will be
+ * notified when image data is either partially or completely loaded.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see #addImageLoaderListener(ImageLoaderListener)
+ */
+public void removeImageLoaderListener(ImageLoaderListener listener) {
+    if (listener is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    if (imageLoaderListeners.length == 0 ) return;
+    tango.core.Array.remove( imageLoaderListeners, listener, delegate bool(ImageLoaderListener l1, ImageLoaderListener l2 ){ return l1 is l2; });
+}
+
+/**
+ * Returns <code>true</code> if the receiver has image loader
+ * listeners, and <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if there are <code>ImageLoaderListener</code>s, and <code>false</code> otherwise
+ *
+ * @see #addImageLoaderListener(ImageLoaderListener)
+ * @see #removeImageLoaderListener(ImageLoaderListener)
+ */
+public bool hasListeners() {
+    return imageLoaderListeners.length > 0;
+}
+
+/**
+ * Notifies all image loader listeners that an image loader event
+ * has occurred. Pass the specified event object to each listener.
+ *
+ * @param event the <code>ImageLoaderEvent</code> to send to each <code>ImageLoaderListener</code>
+ */
+public void notifyListeners(ImageLoaderEvent event) {
+    if (!hasListeners()) return;
+    foreach( listener; imageLoaderListeners ){
+        listener.imageDataLoaded(event);
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageLoaderEvent.d	Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.graphics.ImageLoaderEvent;
+
+
+public import dwt.internal.DWTEventObject;
+public import dwt.graphics.ImageLoader;
+public import dwt.graphics.ImageData;
+
+import tango.text.convert.Format;
+
+/**
+ * Instances of this class are sent as a result of the incremental
+ * loading of image data.
+ * <p>
+ * <b>Notes:</b>
+ * </p><ul>
+ * <li>The number of events which will be sent when loading images
+ * is not constant. It varies by image type, and for JPEG images it
+ * varies from image to image.</li>
+ * <li>For image sources which contain multiple images, the
+ * <code>endOfImage</code> flag in the event will be set to true
+ * after each individual image is loaded.</li>
+ * </ul>
+ *
+ * @see ImageLoader
+ * @see ImageLoaderListener
+ */
+
+public class ImageLoaderEvent : DWTEventObject {
+
+    /**
+     * if the <code>endOfImage</code> flag is false, then this is a
+     * partially complete copy of the current <code>ImageData</code>,
+     * otherwise this is a completely loaded <code>ImageData</code>
+     */
+    public ImageData imageData;
+
+    /**
+     * the zero-based count of image data increments -- this is
+     * equivalent to the number of events that have been generated
+     * while loading a particular image
+     */
+    public int incrementCount;
+
+    /**
+     * If this flag is true, then the current image data has been
+     * completely loaded, otherwise the image data is only partially
+     * loaded, and further ImageLoader events will occur unless an
+     * exception is thrown
+     */
+    public bool endOfImage;
+
+    //static final long serialVersionUID = 3257284738325558065L;
+
+/**
+ * Constructs a new instance of this class given the event source and
+ * the values to store in its fields.
+ *
+ * @param source the ImageLoader that was loading when the event occurred
+ * @param imageData the image data for the event
+ * @param incrementCount the image data increment for the event
+ * @param endOfImage the end of image flag for the event
+ */
+public this(ImageLoader source, ImageData imageData, int incrementCount, bool endOfImage) {
+    super(source);
+    this.imageData = imageData;
+    this.incrementCount = incrementCount;
+    this.endOfImage = endOfImage;
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the event
+ */
+public char[] toString () {
+    return Format( "ImageLoaderEvent {source={} imageData={} incrementCount={} endOfImage={}}", source, imageData, incrementCount, endOfImage); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/ImageLoaderListener.d	Sat Jan 26 19:05:32 2008 +0100
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.graphics.ImageLoaderListener;
+
+
+public import dwt.graphics.ImageLoaderEvent;
+public import dwt.internal.DWTEventListener;
+
+/**
+ * Classes which implement this interface provide methods
+ * that deal with the incremental loading of image data.
+ * <p>
+ * After creating an instance of a class that implements
+ * this interface it can be added to an image loader using the
+ * <code>addImageLoaderListener</code> method and removed using
+ * the <code>removeImageLoaderListener</code> method. When
+ * image data is either partially or completely loaded, this
+ * method will be invoked.
+ * </p>
+ *
+ * @see ImageLoader
+ * @see ImageLoaderEvent
+ */
+
+public interface ImageLoaderListener : DWTEventListener {
+
+/**
+ * Sent when image data is either partially or completely loaded.
+ * <p>
+ * The timing of when this method is called varies depending on
+ * the format of the image being loaded.
+ * </p>
+ *
+ * @param e an event containing information about the image loading operation
+ */
+public void imageDataLoaded(ImageLoaderEvent e);
+
+}
--- a/dwt/internal/gdip/Gdip.d	Sat Jan 26 16:45:05 2008 +0100
+++ b/dwt/internal/gdip/Gdip.d	Sat Jan 26 19:05:32 2008 +0100
@@ -12,40 +12,8 @@
 
 import dwt.internal.Library;
 import dwt.internal.Platform;
-
-public struct RectF {
-    public float X;
-    public float Y;
-    public float Width;
-    public float Height;
-}
+import dwt.internal.win32.OS;
 
-public struct Rect {
-    public int X;
-    public int Y;
-    public int Width;
-    public int Height;
-}
-public struct PointF {
-    public float X;
-    public float Y;
-}
-
-public struct ColorPalette {
-    public int Flags;
-    public int Count;
-    public int[1] Entries;
-}
-public struct BitmapData {
-    public int Width;
-    public int Height;
-    public int Stride;
-    public int PixelFormat;
-    public void* Scan0;
-    public int   Reserved;
-}
-
-struct Matrix{}
 
 
 alias extern(Windows) void function(int level, char *message) DebugEventProc;
@@ -194,6 +162,49 @@
     public static const int WrapModeClamp = 4;
 
 
+public struct RectF {
+    public float X;
+    public float Y;
+    public float Width;
+    public float Height;
+}
+
+public struct Rect {
+    public int X;
+    public int Y;
+    public int Width;
+    public int Height;
+}
+public struct PointF {
+    public float X;
+    public float Y;
+}
+
+public struct ColorPalette {
+    public int Flags;
+    public int Count;
+    public int[1] Entries;
+}
+public struct BitmapData {
+    public int Width;
+    public int Height;
+    public int Stride;
+    public int PixelFormat;
+    public void* Scan0;
+    public int   Reserved;
+}
+
+struct Matrix{}
+struct Bitmap{}
+struct Image{}
+struct Pen{}
+struct Brush{}
+struct Font{}
+struct StringFormat{}
+struct Region{}
+struct GraphicsPath{}
+struct Graphics{}
+struct ImageAttributes{}
 
     public alias API.GdiplusStartup  GdiplusStartup;
     public alias API.GdiplusShutdown GdiplusShutdown;
@@ -210,6 +221,77 @@
     public static extern(Windows) int  Matrix_TransformPoints(Matrix*,PointF*, int);
     public static extern(Windows) int  Matrix_Translate(Matrix*,float, float, float);
 
+    public static extern(Windows) Bitmap* Bitmap_new(HBITMAP hbm, HPALETTE hpal);
+    public static extern(Windows) Bitmap* Bitmap_new(HICON hicon);
+    public static extern(Windows) Bitmap* Bitmap_new(int width, int height, int stride, int format, byte* scan0);
+    public static extern(Windows) Bitmap* Bitmap_new(wchar* filename, int useIcm);
+    public static extern(Windows) void Bitmap_delete(Bitmap* bitmap);
+    // colorBackground is C++ &-reference Color
+    public static extern(Windows) int Bitmap_GetHBITMAP(Bitmap*bitmap, void* colorBackground, HBITMAP* hbmReturn);
+    public static extern(Windows) int Bitmap_GetHICON(Bitmap* bitmap, HICON* hicon);
+    public static extern(Windows) int Bitmap_LockBits(Bitmap* bitmap, Rect* rect, int flags, int pixelFormat, BitmapData* lockedBitmapData);
+    public static extern(Windows) int Bitmap_UnlockBits(Bitmap* bitmap, BitmapData* lockedBitmapData);
+
+    public static extern(Windows) BitmapData* BitmapData_new();
+    public static extern(Windows) void BitmapData_delete(BitmapData* bitmapData);
+
+    public static extern(Windows) int Image_GetLastStatus(Image* image);
+    public static extern(Windows) int Image_GetPixelFormat(Image* image);
+    public static extern(Windows) uint Image_GetWidth(Image* image);
+    public static extern(Windows) uint Image_GetHeight(Image* image);
+    public static extern(Windows) int Image_GetPalette(Image* image, ColorPalette* palette, int size);
+    public static extern(Windows) int Image_GetPaletteSize(Image* image);
+
+alias extern(Windows) int function(void*) ImageAbort;
+alias ImageAbort DrawImageAbort;
+    public static extern(Windows) Graphics* Graphics_new(HDC hdc);
+    public static extern(Windows) void Graphics_delete(Graphics* graphics);
+    public static extern(Windows) int Graphics_DrawArc(Graphics* graphics, Pen* pen, int x, int y, int width, int height, float startAngle, float sweepAngle);
+    public static extern(Windows) int Graphics_DrawEllipse(Graphics* graphics, Pen* pen, int x, int y, int width, int height);
+    public static extern(Windows) int Graphics_DrawImage(Graphics* graphics, Image* image, int x, int y);
+    public static extern(Windows) int Graphics_DrawImage(Graphics* graphics, Image* image, Rect* destRect, int srcx, int srcy, int srcwidth, int srcheight, int srcUnit, ImageAttributes* imageAttributes, DrawImageAbort callback, void* callbackData);
+    public static extern(Windows) int Graphics_DrawLine(Graphics* graphics, Pen* pen, int x1, int y1, int x2, int y2);
+    public static extern(Windows) int Graphics_DrawLines(Graphics* graphics, Pen* pen, PointF* points, int count);
+    public static extern(Windows) int Graphics_DrawPath(Graphics* graphics, Pen* pen, GraphicsPath* path);
+    public static extern(Windows) int Graphics_DrawPolygon(Graphics* graphics, Pen* pen, int[] points, int count);
+    public static extern(Windows) int Graphics_DrawRectangle(Graphics* graphics, Pen* pen, int x, int y, int width, int height);
+    public static extern(Windows) int Graphics_DrawString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, Brush* brush);
+    public static extern(Windows) int Graphics_DrawString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, StringFormat* format, Brush* brush);
+    public static extern(Windows) int Graphics_FillEllipse(Graphics* graphics, Brush* brush, int x, int y, int width, int height);
+    public static extern(Windows) int Graphics_FillPath(Graphics* graphics, Brush* brush, GraphicsPath* path);
+    public static extern(Windows) void Graphics_Flush(Graphics* graphics, int intention);
+    public static extern(Windows) int Graphics_FillPie(Graphics* graphics, Brush* brush, int x, int y, int width, int height, float startAngle, float sweepAngle);
+    public static extern(Windows) int Graphics_FillPolygon(Graphics* graphics, Brush* brush, int[] points, int count, int fillMode);
+    public static extern(Windows) int Graphics_FillRectangle(Graphics* graphics, Brush* brush, int x, int y, int width, int height);
+    public static extern(Windows) int Graphics_GetClipBounds(Graphics* graphics, RectF rect);
+    public static extern(Windows) int Graphics_GetClipBounds(Graphics* graphics, Rect rect);
+    public static extern(Windows) int Graphics_GetClip(Graphics* graphics, Region* region);
+    public static extern(Windows) HDC Graphics_GetHDC(Graphics* graphics);
+    public static extern(Windows) void Graphics_ReleaseHDC(Graphics* graphics, HDC hdc);
+    public static extern(Windows) int Graphics_GetInterpolationMode(Graphics* graphics);
+    public static extern(Windows) int Graphics_GetSmoothingMode(Graphics* graphics);
+    public static extern(Windows) int Graphics_GetTextRenderingHint(Graphics* graphics);
+    public static extern(Windows) int Graphics_GetTransform(Graphics* graphics, Matrix* matrix);
+    public static extern(Windows) int Graphics_GetVisibleClipBounds(Graphics* graphics, Rect rect);
+    public static extern(Windows) int Graphics_MeasureString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, RectF boundingBox);
+    public static extern(Windows) int Graphics_MeasureString(Graphics* graphics, wchar* string, int length, Font* font, PointF origin, StringFormat* format, RectF boundingBox);
+    public static extern(Windows) int Graphics_ResetClip(Graphics* graphics);
+    public static extern(Windows) int Graphics_Restore(Graphics* graphics, int gstate);
+    public static extern(Windows) int Graphics_Save(Graphics* graphics);
+    public static extern(Windows) int Graphics_ScaleTransform(Graphics* graphics, float sx, float sy, int order);
+    public static extern(Windows) int Graphics_SetClip(Graphics* graphics, HRGN hrgn, int combineMode);
+    public static extern(Windows) int Graphics_SetClip(Graphics* graphics, GraphicsPath* path);
+    public static extern(Windows) int Graphics_SetClip(Graphics* graphics, Rect rect, int combineMode);
+    public static extern(Windows) int Graphics_SetCompositingQuality(Graphics* graphics, int compositingQuality);
+    public static extern(Windows) int Graphics_SetPageUnit(Graphics* graphics, int unit);
+    public static extern(Windows) int Graphics_SetPixelOffsetMode(Graphics* graphics, int pixelOffsetMode);
+    public static extern(Windows) int Graphics_SetSmoothingMode(Graphics* graphics, int smoothingMode);
+    public static extern(Windows) int Graphics_SetTransform(Graphics* graphics, Matrix* matrix);
+    public static extern(Windows) int Graphics_SetInterpolationMode(Graphics* graphics, int mode);
+    public static extern(Windows) int Graphics_SetTextRenderingHint(Graphics* graphics, int mode);
+    public static extern(Windows) int Graphics_TranslateTransform(Graphics* graphics, float dx, float dy, int order);
+
+
 /++
 /** 64 bit */
 public static final native int ColorPalette_sizeof();