Mercurial > projects > dwt-win
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; +} + +}