view dwt/graphics/Image.d @ 129:ad4e1fe71a5a

Fixed runtime errors
author Jacob Carlborg <doob@me.com>
date Sun, 18 Jan 2009 18:39:46 +0100
parents cfa563df4fdd
children 0ba75290f8ce
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     
 * Port to the D programming language:
 *     Jacob Carlborg <doob@me.com>
 *******************************************************************************/
module dwt.graphics.Image;

 
import dwt.dwthelper.InputStream;

import dwt.DWT;
import dwt.DWTError;
import dwt.DWTException;
import dwt.internal.C;
import dwt.internal.cocoa.NSAffineTransform;
import dwt.internal.cocoa.NSAutoreleasePool;
import dwt.internal.cocoa.NSBitmapImageRep;
import dwt.internal.cocoa.NSGraphicsContext;
import dwt.internal.cocoa.NSImage;
import dwt.internal.cocoa.NSImageRep;
import dwt.internal.cocoa.NSSize;
import dwt.internal.cocoa.NSThread;
import dwt.internal.cocoa.OS;

import tango.text.convert.Format;

import dwt.dwthelper.utils;
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.internal.objc.cocoa.Cocoa;
import objc = dwt.internal.objc.runtime;

import tango.io.Stdout;
 
/**
 * 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
 * @see <a href="http://www.eclipse.org/swt/snippets/#image">Image snippets</a>
 * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Examples: GraphicsExample, ImageAnalyzer</a>
 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
 */
public final class Image : Resource, Drawable {

    alias Resource.init_ init_;
    
    /**
     * 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 NSImage handle;
    NSBitmapImageRep imageRep;
    
    /**
     * specifies the transparent pixel
     */
    int transparentPixel = -1;
    
    /**
     * The GC the image is currently selected in.
     */
    GC memGC;

    /**
     * The alpha data of the image.
     */
    byte[] alphaData;
    
    /**
     * The global alpha value to be used for every pixel.
     */
    int alpha = -1;
    
    /**
     * The width of the image.
     */
    int width = -1;
    
    /**
     * The height of the image.
     */
    int height = -1;
    
    /**
     * Specifies the default scanline padding.
     */
    static final int DEFAULT_SCANLINE_PAD = 4;

this(Device device) {
    super(device);
}

/**
 * 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) {
    super(device);
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        init_(width, height);
        init_();
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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>{@link DWT#IMAGE_COPY}</b></dt>
 * <dd>the result is an identical copy of srcImage</dd>
 * <dt><b>{@link DWT#IMAGE_DISABLE}</b></dt>
 * <dd>the result is a copy of srcImage which has a <em>disabled</em> look</dd>
 * <dt><b>{@link DWT#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) {
    super(device);
    if (srcImage is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
    if (srcImage.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
    switch (flag) {
        case DWT.IMAGE_COPY:
        case DWT.IMAGE_DISABLE:
        case DWT.IMAGE_GRAY:
            break;
        default:
            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
    }

    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        device = this.device;
        this.type = srcImage.type;
        /* Get source image size */
        NSSize size = srcImage.handle.size();
    int width = cast(int)size.width;
    int height = cast(int)size.height;
        NSBitmapImageRep srcRep = srcImage.imageRep;
    NSInteger bpr = srcRep.bytesPerRow();

        /* Copy transparent pixel and alpha data when necessary */
        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);
        }

        /* Create the image */
    handle = cast(NSImage)(new NSImage()).alloc();
        handle = handle.initWithSize(size);
    NSBitmapImageRep rep = imageRep = cast(NSBitmapImageRep)(new NSBitmapImageRep()).alloc();
    rep = rep.initWithBitmapDataPlanes(null, width, height, srcRep.bitsPerSample(), srcRep.samplesPerPixel(), srcRep.samplesPerPixel() is 4, srcRep.isPlanar(), OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, srcRep.bytesPerRow(), srcRep.bitsPerPixel());
        handle.addRepresentation(rep);

    ubyte* data = rep.bitmapData();
    OS.memmove(data, srcImage.imageRep.bitmapData(), width * height * 4);
        if (flag !is DWT.IMAGE_COPY) {

            /* Apply transformation */
            switch (flag) {
            case DWT.IMAGE_DISABLE: {
                Color zeroColor = device.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
                RGB zeroRGB = zeroColor.getRGB();
                byte zeroRed = cast(byte)zeroRGB.red;
                byte zeroGreen = cast(byte)zeroRGB.green;
                byte zeroBlue = cast(byte)zeroRGB.blue;
                Color oneColor = device.getSystemColor(DWT.COLOR_WIDGET_BACKGROUND);
                RGB oneRGB = oneColor.getRGB();
                byte oneRed = cast(byte)oneRGB.red;
                byte oneGreen = cast(byte)oneRGB.green;
                byte oneBlue = cast(byte)oneRGB.blue;
                byte[] line = new byte[bpr];
                for (int y=0; y<height; y++) {
                    OS.memmove(line.ptr, data + (y * bpr), bpr);
                    int offset = 0;
                    for (int x=0; x<width; x++) {
                        int red = line[offset+1] & 0xFF;
                        int green = line[offset+2] & 0xFF;
                        int blue = line[offset+3] & 0xFF;
                        int intensity = red * red + green * green + blue * blue;
                        if (intensity < 98304) {
                            line[offset+1] = zeroRed;
                            line[offset+2] = zeroGreen;
                            line[offset+3] = zeroBlue;
                        } else {
                            line[offset+1] = oneRed;
                            line[offset+2] = oneGreen;
                            line[offset+3] = oneBlue;
                        }
                        offset += 4;
                    }
                    OS.memmove(data + (y * bpr), line.ptr, bpr);
                }
                break;
            }
            case DWT.IMAGE_GRAY: {          
                byte[] line = new byte[bpr];
                for (int y=0; y<height; y++) {
                    OS.memmove(line.ptr, data + (y * bpr), bpr);
                    int offset = 0;
                    for (int x=0; x<width; x++) {
                        int red = line[offset+1] & 0xFF;
                        int green = line[offset+2] & 0xFF;
                        int blue = line[offset+3] & 0xFF;
                        byte intensity = cast(byte)((red+red+green+green+green+green+green+blue) >> 3);
                        line[offset+1] = line[offset+2] = line[offset+3] = intensity;
                        offset += 4;
                    }
                    OS.memmove(data + (y * bpr), line.ptr, bpr);
                }
                break;
            }
            }
        }
    init_();
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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) {
    super(device);
    if (bounds is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        init_(bounds.width, bounds.height);
        init_();
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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) {
    super(device);
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        init_(data);
        init_();
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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) {
    super(device);
    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);
    }
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        mask = ImageData.convertMask(mask);
        ImageData image = new ImageData(source.width, source.height, source.depth, source.palette, source.scanlinePad, source.data);
        image.maskPad = mask.scanlinePad;
        image.maskData = mask.data;
    init_(image);
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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) {
    super(device);
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        init_(new ImageData(stream));
        init_();
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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, String filename) {
    super(device);
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        init_(new ImageData(filename));
        init_();
    } finally {
        if (pool !is null) pool.release();
    }
}

void createAlpha () {
    if (transparentPixel is -1 && alpha is -1 && alphaData is null) return;
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        NSSize size = handle.size();
    int height = cast(int)size.height;
    NSInteger bpr = imageRep.bytesPerRow();
    size_t dataSize = height * bpr;
        byte[] srcData = new byte[dataSize];
    OS.memmove(srcData.ptr, imageRep.bitmapData(), dataSize);
        if (transparentPixel !is -1) {
            for (int i=0; i<dataSize; i+=4) {
                int pixel = ((srcData[i+1] & 0xFF) << 16) | ((srcData[i+2] & 0xFF) << 8) | (srcData[i+3] & 0xFF);
            srcData[i] = cast(byte)(pixel is transparentPixel ? 0 : 0xFF); 
            }
        } else if (alpha !is -1) {
        byte a = cast(byte)this.alpha;
            for (int i=0; i<dataSize; i+=4) {
                srcData[i] = a;             
            }
        } else {
        int width = cast(int)size.width;
            int offset = 0, alphaOffset = 0;
            for (int y = 0; y<height; y++) {
                for (int x = 0; x<width; x++) {
                    srcData[offset] = alphaData[alphaOffset];
                    offset += 4;
                    alphaOffset += 1;
                }
            }
        }
    OS.memmove(imageRep.bitmapData(), srcData.ptr, dataSize);
    } finally {
        if (pool !is null) pool.release();
    }
}

void destroy() {
    if (memGC !is null) memGC.dispose();
    if (imageRep !is null) imageRep.release();
    handle.release();
    imageRep = null;
    handle = null;
    memGC = 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 (!( null !is cast(Image)object )) return false;
    Image image = cast(Image)object;
    return device is image.device && handle is image.handle &&
        transparentPixel is image.transparentPixel;
}

alias opEquals equals;

/**
 * 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;
    int red = (transparentPixel >> 16) & 0xFF;
    int green = (transparentPixel >> 8) & 0xFF;
    int blue = (transparentPixel >> 0) & 0xFF;
    return Color.cocoa_new(device, [red / 255f, green / 255f, blue / 255f, 1]);
}

/**
 * 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);
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        if (width !is -1 && height !is -1) {
            return new Rectangle(0, 0, width, height);
        }
        NSSize size = handle.size();
    return new Rectangle(0, 0, width = cast(int)size.width, height = cast(int)size.height);
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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);
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        NSSize size = handle.size();
    int width = cast(int)size.width;
    int height = cast(int)size.height;
        NSBitmapImageRep imageRep = this.imageRep;
    NSInteger  bpr = imageRep.bytesPerRow();
    NSInteger  bpp = imageRep.bitsPerPixel();
    size_t dataSize = height * bpr;
        byte[] srcData = new byte[dataSize];
    OS.memmove(srcData.ptr, imageRep.bitmapData(), dataSize);

        PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF);
        ImageData data = new ImageData(width, height, cast(int)/*64*/bpp, palette, 4, srcData);
        data.bytesPerLine = cast(int)/*64*/bpr;

        data.transparentPixel = transparentPixel;
        if (transparentPixel is -1 && type is DWT.ICON) {
            /* Get the icon mask data */
            int maskPad = 2;
            int maskBpl = (((width + 7) / 8) + (maskPad - 1)) / maskPad * maskPad;
            byte[] maskData = new byte[height * maskBpl];
            int offset = 0, maskOffset = 0;
            for (int y = 0; y<height; y++) {
                for (int x = 0; x<width; x++) {
                    if (srcData[offset] !is 0) {
                        maskData[maskOffset + (x >> 3)] |= (1 << (7 - (x & 0x7)));
                    } else {
                        maskData[maskOffset + (x >> 3)] &= ~(1 << (7 - (x & 0x7)));
                    }
                    offset += 4;
                }
                maskOffset += maskBpl;
            }
            data.maskData = maskData;
            data.maskPad = maskPad;
        }
        for (int i = 0; i < srcData.length; i+= 4) {
            srcData[i] = 0;
        }
        data.alpha = alpha;
        if (alpha is -1 && alphaData !is null) {
            data.alphaData = new byte[alphaData.length];
            System.arraycopy(alphaData, 0, data.alphaData, 0, alphaData.length);
        }
        return data;
    } finally {
        if (pool !is null) pool.release();
    }
}

/**  
 * 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
 * @param data the OS data for the image
 *
 * @private
 */
public static Image cocoa_new(Device device, int type, NSImage nsImage) {
    Image image = new Image(device);
    image.type = type;
    image.handle = nsImage;
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        NSImageRep rep = nsImage.bestRepresentationForDevice(null);
        if (rep.isKindOfClass(cast(objc.Class)OS.class_NSBitmapImageRep)) { 
            rep.retain();
            image.imageRep = new NSBitmapImageRep(rep.id);
        }
        return image;
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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 handle !is null ? cast(hash_t) handle.id : 0;
}

alias toHash hashCode;

void init_(int width, int height) {
    if (width <= 0 || height <= 0) {
        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
    }
    this.type = DWT.BITMAP;
    this.width = width;
    this.height = height;

    handle = cast(NSImage)(new NSImage()).alloc();
    NSSize size = NSSize();
    size.width = width;
    size.height = height;
    handle = handle.initWithSize(size);
    NSBitmapImageRep rep = imageRep = cast(NSBitmapImageRep)(new NSBitmapImageRep()).alloc();
    rep = rep.initWithBitmapDataPlanes(null, width, height, 8, 3, false, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, width * 4, 32);
    OS.memset(rep.bitmapData(), 0xFF, width * height * 4);
    handle.addRepresentation(rep);
//  rep.release();
}

void init_(ImageData image) {
    if (image is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
    this.width = image.width;
    this.height = image.height;
    PaletteData palette = image.palette;
    if (!(((image.depth is 1 || image.depth is 2 || image.depth is 4 || image.depth is 8) && !palette.isDirect) ||
            ((image.depth is 8) || (image.depth is 16 || image.depth is 24 || image.depth is 32) && palette.isDirect)))
                DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
    
    /* Create the image */
    size_t dataSize = width * height * 4;
    
    /* Initialize data */
    int bpr = width * 4;
    byte[] buffer = new byte[dataSize];
    if (palette.isDirect) {
        ImageData.blit(ImageData.BLIT_SRC,
            image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, palette.redMask, palette.greenMask, palette.blueMask,
            ImageData.ALPHA_OPAQUE, null, 0, 0, 0, 
            buffer, 32, bpr, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF,
            false, false);
    } else {
        RGB[] rgbs = palette.getRGBs();
        int length = rgbs.length;
        byte[] srcReds = new byte[length];
        byte[] srcGreens = new byte[length];
        byte[] srcBlues = new byte[length];
        for (int i = 0; i < rgbs.length; i++) {
            RGB rgb = rgbs[i];
            if (rgb is null) continue;
            srcReds[i] = cast(byte)rgb.red;
            srcGreens[i] = cast(byte)rgb.green;
            srcBlues[i] = cast(byte)rgb.blue;
        }
        ImageData.blit(ImageData.BLIT_SRC,
            image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, srcReds, srcGreens, srcBlues,
            ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
            buffer, 32, bpr, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF,
            false, false);
    }
    
    /* Initialize transparency */
    int transparency = image.getTransparencyType();
    bool hasAlpha = transparency !is DWT.TRANSPARENCY_NONE;
    if (transparency is DWT.TRANSPARENCY_MASK || image.transparentPixel !is -1) {
        this.type = image.transparentPixel !is -1 ? DWT.BITMAP : DWT.ICON;
        if (image.transparentPixel !is -1) {
            int transRed = 0, transGreen = 0, transBlue = 0;
            if (palette.isDirect) {
                RGB rgb = palette.getRGB(image.transparentPixel);
                transRed = rgb.red;
                transGreen = rgb.green;
                transBlue = rgb.blue;
            } else {
                RGB[] rgbs = palette.getRGBs();
                if (image.transparentPixel < rgbs.length) {
                    RGB rgb = rgbs[image.transparentPixel];
                    transRed = rgb.red;
                    transGreen = rgb.green;
                    transBlue = rgb.blue;               
                }
            }
            transparentPixel = transRed << 16 | transGreen << 8 | transBlue;
        }
        ImageData maskImage = image.getTransparencyMask();
        byte[] maskData = maskImage.data;
        int maskBpl = maskImage.bytesPerLine;
        int offset = 0, maskOffset = 0;
        for (int y = 0; y<height; y++) {
            for (int x = 0; x<width; x++) {
                buffer[offset] = ((maskData[maskOffset + (x >> 3)]) & (1 << (7 - (x & 0x7)))) !is 0 ? cast(byte)0xff : 0;
                offset += 4;
            }
            maskOffset += maskBpl;
        }
    } else {
        this.type = DWT.BITMAP;
        if (image.alpha !is -1) {
            hasAlpha = true;
            this.alpha = image.alpha;
            byte a = cast(byte)this.alpha;
            for (int dataIndex=0; dataIndex<buffer.length; dataIndex+=4) {
                buffer[dataIndex] = a;              
            }
        } else if (image.alphaData !is null) {
            this.alphaData = new byte[image.alphaData.length];
            System.arraycopy(image.alphaData, 0, this.alphaData, 0, alphaData.length);
            int offset = 0, alphaOffset = 0;
            for (int y = 0; y<height; y++) {
                for (int x = 0; x<width; x++) {
                    buffer[offset] = alphaData[alphaOffset];
                    offset += 4;
                    alphaOffset += 1;
                }
            }
        }
    }
    handle = cast(NSImage)(new NSImage()).alloc();
    NSSize size = NSSize();
    size.width = width;
    size.height = height;
    handle = handle.initWithSize(size);
    NSBitmapImageRep rep = imageRep = cast(NSBitmapImageRep)(new NSBitmapImageRep()).alloc();
    rep = rep.initWithBitmapDataPlanes(null, width, height, 8, hasAlpha ? 4 : 3, hasAlpha, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, bpr, 32);
    OS.memmove(rep.bitmapData(), buffer.ptr, dataSize); 
    handle.addRepresentation(rep);
//  rep.release();
}

/**  
 * 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 objc.id internal_new_GC (GCData data) {
    if (handle is null) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
    if (type !is DWT.BITMAP || memGC !is null) {
        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
    }
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        NSBitmapImageRep rep = imageRep;
        if (imageRep.hasAlpha()) {
        NSInteger bpr = width * 4;
        rep = cast(NSBitmapImageRep)(new NSBitmapImageRep()).alloc();
        ubyte* bitmapData = imageRep.bitmapData();
        if (data.bitmapDataAddress !is null) OS.free(data.bitmapDataAddress);
        data.bitmapDataAddress = cast(ubyte*) OS.malloc(C.PTR_SIZEOF);
        OS.memmove(data.bitmapDataAddress, bitmapData, C.PTR_SIZEOF);
            rep = rep.initWithBitmapDataPlanes(&data.bitmapDataAddress, width, height, 8, 3, false, false, OS.NSDeviceRGBColorSpace, OS.NSAlphaFirstBitmapFormat , bpr, 32);
            rep.autorelease();
        }
        handle.setCacheMode(OS.NSImageCacheNever);
        NSGraphicsContext context = NSGraphicsContext.graphicsContextWithBitmapImageRep(rep);
        NSGraphicsContext.setCurrentContext(context);
        NSAffineTransform transform = NSAffineTransform.transform();
        NSSize size = handle.size();
        transform.translateXBy(0, size.height);
        transform.scaleXBy(1, -1);
        transform.set();
        if (data !is null) {
            int mask = DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
            if ((data.style & mask) is 0) {
                data.style |= DWT.LEFT_TO_RIGHT;
            }
            data.device = device;
            data.background = device.COLOR_WHITE.handle;
            data.foreground = device.COLOR_BLACK.handle;
            data.font = device.systemFont;
            data.image = this;
        }
    return context.id;
    } finally {
        if (pool !is null) pool.release();
    }
}

/**  
 * 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 (objc.id context, GCData data) {
    NSAutoreleasePool pool = null;
    if (!NSThread.isMainThread()) pool = cast(NSAutoreleasePool) (new NSAutoreleasePool()).alloc().init();
    try {
        if (data.bitmapDataAddress !is null) OS.free(data.bitmapDataAddress);
        data.bitmapDataAddress = null;
//      handle.setCacheMode(OS.NSImageCacheDefault);
    } finally {
        if (pool !is null) pool.release();
    }
}

/**
 * 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) {
    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;
//  byte red = cast(byte)((transparentPixel >> 16) & 0xFF);
//  byte green = cast(byte)((transparentPixel >> 8) & 0xFF);
//  byte blue = cast(byte)((transparentPixel >> 0) & 0xFF);
//  byte newRed = cast(byte)(cast(int)(color.handle[0] * 255) & 0xFF);
//  byte newGreen = cast(byte)(cast(int)(color.handle[1] * 255) & 0xFF);
//  byte newBlue = cast(byte)(cast(int)(color.handle[2] * 255) & 0xFF);
//  int height = OS.CGImageGetHeight(handle);
//  int bpl = OS.CGImageGetBytesPerRow(handle);
//  byte[] line = new byte[bpl];
//  for (int i = 0, offset = 0; i < height; i++, offset += bpl) {
//      OS.memmove(line, data + offset, bpl);
//      for (int j = 0; j  < line.length; j += 4) {
//          if (line[j+ 1] is red && line[j + 2] is green && line[j + 3] is blue) {
//              line[j + 1] = newRed;
//              line[j + 2] = newGreen;
//              line[j + 3] = newBlue;
//          }
//      }
//      OS.memmove(data + offset, line, bpl);
//  }
//  transparentPixel = (newRed & 0xFF) << 16 | (newGreen & 0xFF) << 8 | (newBlue & 0xFF);
}

/**
 * Returns a string containing a concise, human-readable
 * description of the receiver.
 *
 * @return a string representation of the receiver
 */
public String toString () {
    if (isDisposed()) return "Image {*DISPOSED*}";
    return Format("Image {{}{}" , handle , "}");
}

}