diff dwt/graphics/Image.d @ 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
children f5482da87ed8
line wrap: on
line diff
--- /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;
+}
+
+}