changeset 18:437c59646731

Color
author Frank Benoit <benoit@tionex.de>
date Sat, 26 Jan 2008 10:22:58 +0100
parents eca0c8261b9f
children ded98545bb1f
files dwt/graphics/Color.d dwt/graphics/Device.d
diffstat 2 files changed, 382 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/Color.d	Sat Jan 26 10:22:58 2008 +0100
@@ -0,0 +1,346 @@
+/*******************************************************************************
+ * 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.Color;
+
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.internal.win32.OS;
+
+import dwt.graphics.Resource;
+import dwt.graphics.RGB;
+import dwt.graphics.Device;
+
+import tango.text.convert.Format;
+
+/**
+ * Instances of this class manage the operating system resources that
+ * implement DWT's RGB color model. To create a color you can either
+ * specify the individual color components as integers in the range
+ * 0 to 255 or provide an instance of an <code>RGB</code>.
+ * <p>
+ * Application code must explicitly invoke the <code>Color.dispose()</code>
+ * method to release the operating system resources managed by each instance
+ * when those instances are no longer required.
+ * </p>
+ *
+ * @see RGB
+ * @see Device#getSystemColor
+ */
+
+public final class Color : Resource {
+
+    /**
+     * the handle to the OS color 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 COLORREF handle;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+this() {
+}
+
+/**
+ * Constructs a new instance of this class given a device and the
+ * desired red, green and blue values expressed as ints in the range
+ * 0 to 255 (where 0 is black and 255 is full brightness). On limited
+ * color devices, the color instance created by this call may not have
+ * the same RGB values as the ones specified by the arguments. The
+ * RGB values on the returned instance will be the color values of
+ * the operating system color.
+ * <p>
+ * You must dispose the color when it is no longer required.
+ * </p>
+ *
+ * @param device the device on which to allocate the color
+ * @param red the amount of red in the color
+ * @param green the amount of green in the color
+ * @param blue the amount of blue in the color
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li>
+ * </ul>
+ *
+ * @see #dispose
+ */
+public this (Device device, int red, int green, int blue) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    init(device, red, green, blue);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs a new instance of this class given a device and an
+ * <code>RGB</code> describing the desired red, green and blue values.
+ * On limited color devices, the color instance created by this call
+ * may not have the same RGB values as the ones specified by the
+ * argument. The RGB values on the returned instance will be the color
+ * values of the operating system color.
+ * <p>
+ * You must dispose the color when it is no longer required.
+ * </p>
+ *
+ * @param device the device on which to allocate the color
+ * @param rgb the RGB values of the desired color
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the rgb argument is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the red, green or blue components of the argument are not between 0 and 255</li>
+ * </ul>
+ *
+ * @see #dispose
+ */
+public this (Device device, RGB rgb) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (rgb is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    init(device, rgb.red, rgb.green, rgb.blue);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Disposes of the operating system resources associated with
+ * the color. Applications must dispose of all colors which
+ * they allocate.
+ */
+public void dispose() {
+    if (handle is -1) return;
+    if (device.isDisposed()) return;
+
+    /*
+     * If this is a palette-based device,
+     * Decrease the reference count for this color.
+     * If the reference count reaches 0, the slot may
+     * be reused when another color is allocated.
+     */
+    auto hPal = device.hPalette;
+    if (hPal !is null) {
+        int index = OS.GetNearestPaletteIndex(hPal, handle);
+        int[] colorRefCount = device.colorRefCount;
+        if (colorRefCount[index] > 0) {
+            colorRefCount[index]--;
+        }
+    }
+    handle = -1;
+    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 bool equals (Object object) {
+    if (object is this) return true;
+    if (!(cast(Color)object)) return false;
+    Color color = cast(Color) object;
+    return device is color.device && (handle & 0xFFFFFF) is (color.handle & 0xFFFFFF);
+}
+
+/**
+ * Returns the amount of blue in the color, from 0 to 255.
+ *
+ * @return the blue component of the color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getBlue () {
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return (handle & 0xFF0000) >> 16;
+}
+
+/**
+ * Returns the amount of green in the color, from 0 to 255.
+ *
+ * @return the green component of the color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getGreen () {
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return (handle & 0xFF00) >> 8 ;
+}
+
+/**
+ * Returns the amount of red in the color, from 0 to 255.
+ *
+ * @return the red component of the color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getRed () {
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return handle & 0xFF;
+}
+
+/**
+ * Returns an <code>RGB</code> representing the receiver.
+ *
+ * @return the RGB for the color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public RGB getRGB () {
+    if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
+    return new RGB(cast(int)handle & 0xFF,cast(int) (handle & 0xFF00) >> 8,cast(int) (handle & 0xFF0000) >> 16);
+}
+
+/**
+ * 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 int hashCode () {
+    return handle;
+}
+
+/**
+ * Allocates the operating system resources associated
+ * with the receiver.
+ *
+ * @param device the device on which to allocate the color
+ * @param red the amount of red in the color
+ * @param green the amount of green in the color
+ * @param blue the amount of blue in the color
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li>
+ * </ul>
+ *
+ * @see #dispose
+ */
+void init(Device device, int red, int green, int blue) {
+    if (red > 255 || red < 0 || green > 255 || green < 0 || blue > 255 || blue < 0) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    this.device = device;
+    handle = (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16);
+
+    /* If this is not a palette-based device, return */
+    auto hPal = device.hPalette;
+    if (hPal is null) return;
+
+    int[] colorRefCount = device.colorRefCount;
+    /* Add this color to the default palette now */
+    /* First find out if the color already exists */
+    int index = OS.GetNearestPaletteIndex(hPal, handle);
+    /* See if the nearest color actually is the color */
+    PALETTEENTRY entry;
+    OS.GetPaletteEntries(hPal, index, 1, &entry);
+    if ((entry.peRed is cast(byte)red) && (entry.peGreen is cast(byte)green) &&
+        (entry.peBlue is cast(byte)blue)) {
+            /* Found the color. Increment the ref count and return */
+            colorRefCount[index]++;
+            return;
+    }
+    /* Didn't find the color, allocate it now. Find the first free entry */
+    int i = 0;
+    while (i < colorRefCount.length) {
+        if (colorRefCount[i] is 0) {
+            index = i;
+            break;
+        }
+        i++;
+    }
+    if (i is colorRefCount.length) {
+        /* No free entries, use the closest one */
+        /* Remake the handle from the actual rgbs */
+        handle = (entry.peRed & 0xFF) | ((entry.peGreen & 0xFF) << 8) |
+                 ((entry.peBlue & 0xFF) << 16);
+    } else {
+        /* Found a free entry */
+        entry.peRed = cast(BYTE)(red & 0xFF);
+        entry.peGreen = cast(BYTE)(green & 0xFF);
+        entry.peBlue = cast(BYTE)(blue & 0xFF);
+        entry.peFlags = 0;
+        OS.SetPaletteEntries(hPal, index, 1, &entry);
+    }
+    colorRefCount[index]++;
+}
+
+/**
+ * Returns <code>true</code> if the color has been disposed,
+ * and <code>false</code> otherwise.
+ * <p>
+ * This method gets the dispose state for the color.
+ * When a color has been disposed, it is an error to
+ * invoke any other method using the color.
+ *
+ * @return <code>true</code> when the color is disposed and <code>false</code> otherwise
+ */
+public bool isDisposed() {
+    return handle is -1;
+}
+
+/**
+ * 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 "Color {*DISPOSED*}"; //$NON-NLS-1$
+    return Format( "Color {{{}, {}, {}}", getRed(), getGreen(), getBlue()); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new color.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Color</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 handle the handle for the color
+ * @return a new color object containing the specified device and handle
+ */
+public static Color win32_new(Device device, int handle) {
+    if (device is null) device = Device.getDevice();
+    Color color = new Color();
+    color.handle = handle;
+    color.device = device;
+    return color;
+}
+
+}
--- a/dwt/graphics/Device.d	Sat Jan 26 09:22:08 2008 +0100
+++ b/dwt/graphics/Device.d	Sat Jan 26 10:22:58 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -20,9 +20,12 @@
     void dispose_Object (Object object) ;
     float computePoints(LOGFONT* logFont, HFONT hFont);
     int computePixels(float height) ;
+    public HPALETTE hPalette;
+        int [] colorRefCount;
+
 }
 /+++
- 
+
 import dwt.DWT;
 import dwt.DWTException;
 import dwt.internal.Callback;
@@ -44,16 +47,16 @@
  * can be drawn on by sending messages to the associated GC.
  */
 public abstract class Device implements Drawable {
-    
+
     /* Debugging */
     public static bool DEBUG;
     bool debug = DEBUG;
     bool tracking = DEBUG;
     Error [] errors;
     Object [] objects;
-    
+
     /**
-     * Palette 
+     * Palette
      * (Warning: This field is platform dependent)
      * <p>
      * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
@@ -64,7 +67,7 @@
      */
     public int hPalette = 0;
     int [] colorRefCount;
-    
+
     /* System Font */
     int systemFont;
 
@@ -81,7 +84,7 @@
     int [] gdipToken;
 
     bool disposed;
-    
+
     final static Object CREATE_LOCK = new Object();
 
     /*
@@ -102,14 +105,14 @@
         try {
             Class.forName ("org.eclipse.swt.widgets.Display"); //$NON-NLS-1$
         } catch (Throwable e) {}
-    }   
+    }
 
 /*
 * TEMPORARY CODE.
 */
 static synchronized Device getDevice () {
     if (DeviceFinder !is null) DeviceFinder.run();
-    Device device = CurrentDevice;  
+    Device device = CurrentDevice;
     CurrentDevice = null;
     return device;
 }
@@ -117,12 +120,12 @@
 /**
  * Constructs a new instance of this class.
  * <p>
- * You must dispose the device when it is no longer required. 
+ * You must dispose the device when it is no longer required.
  * </p>
  *
  * @see #create
  * @see #init
- * 
+ *
  * @since 3.1
  */
 public Device() {
@@ -132,7 +135,7 @@
 /**
  * Constructs a new instance of this class.
  * <p>
- * You must dispose the device when it is no longer required. 
+ * You must dispose the device when it is no longer required.
  * </p>
  *
  * @param data the DeviceData which describes the receiver
@@ -153,7 +156,7 @@
             errors = new Error [128];
             objects = new Object [128];
         }
-        
+
         /* Initialize the system font slot */
         systemFont = getSystemFont().handle;
     }
@@ -228,7 +231,7 @@
 float computePoints(LOGFONT logFont, int hFont) {
     int hDC = internal_new_GC (null);
     int logPixelsY = OS.GetDeviceCaps(hDC, OS.LOGPIXELSY);
-    int pixels = 0; 
+    int pixels = 0;
     if (logFont.lfHeight > 0) {
         /*
          * Feature in Windows. If the lfHeight of the LOGFONT structure
@@ -391,7 +394,7 @@
 /**
  * Returns a rectangle which describes the area of the
  * receiver which is capable of displaying data.
- * 
+ *
  * @return the client area
  *
  * @exception DWTException <ul>
@@ -407,7 +410,7 @@
 /**
  * Returns the bit depth of the screen, which is the number of
  * bits it takes to represent the number of unique colors that
- * the screen is currently capable of displaying. This number 
+ * the screen is currently capable of displaying. This number
  * will typically be one of 1, 8, 15, 16, 24 or 32.
  *
  * @return the depth of the screen
@@ -460,12 +463,12 @@
  */
 public FontData [] getFontList (String faceName, bool scalable) {
     checkDevice ();
-    
+
     /* Create the callback */
     Callback callback = new Callback (this, "EnumFontFamProc", 4); //$NON-NLS-1$
     int lpEnumFontFamProc = callback.getAddress ();
     if (lpEnumFontFamProc is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
-    
+
     /* Initialize the instance variables */
     metrics = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA();
     pixels = new int[nFonts];
@@ -478,10 +481,10 @@
     /* Enumerate */
     int offset = 0;
     int hDC = internal_new_GC (null);
-    if (faceName is null) { 
+    if (faceName is null) {
         /* The user did not specify a face name, so they want all versions of all available face names */
         OS.EnumFontFamilies (hDC, null, lpEnumFontFamProc, scalable ? 1 : 0);
-        
+
         /**
          * For bitmapped fonts, EnumFontFamilies only enumerates once for each font, regardless
          * of how many styles are available. If the user wants bitmapped fonts, enumerate on
@@ -530,7 +533,7 @@
         System.arraycopy (result, 0, newResult, 0, count);
         result = newResult;
     }
-    
+
     /* Clean up */
     callback.dispose ();
     logFonts = null;
@@ -651,7 +654,7 @@
  * If subclasses reimplement this method, they must
  * call the <code>super</code> implementation.
  * </p>
- * 
+ *
  * @see #create
  */
 protected void init () {
@@ -667,7 +670,7 @@
         scripts = new int [piNumScripts [0]];
         OS.MoveMemory (scripts, ppSp [0], scripts.length * 4);
     }
-    
+
     /*
      * If we're not on a device which supports palettes,
      * don't create one.
@@ -676,13 +679,13 @@
     int rc = OS.GetDeviceCaps (hDC, OS.RASTERCAPS);
     int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
     int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
-    
+
     bits *= planes;
     if ((rc & OS.RC_PALETTE) is 0 || bits !is 8) {
         internal_dispose_GC (hDC, null);
         return;
     }
-    
+
     int numReserved = OS.GetDeviceCaps (hDC, OS.NUMRESERVED);
     int numEntries = OS.GetDeviceCaps (hDC, OS.SIZEPALETTE);
 
@@ -702,16 +705,16 @@
 
     /* 4 bytes header + 4 bytes per entry * numEntries entries */
     byte [] logPalette = new byte [4 + 4 * numEntries];
-    
+
     /* 2 bytes = special header */
     logPalette [0] = 0x00;
     logPalette [1] = 0x03;
-    
+
     /* 2 bytes = number of colors, LSB first */
     logPalette [2] = 0;
     logPalette [3] = 1;
 
-    /* 
+    /*
     * Create a palette which contains the system entries
     * as they are located in the system palette.  The
     * MSDN article 'Memory Device Contexts' describes
@@ -731,7 +734,7 @@
     internal_dispose_GC (hDC, null);
     hPalette = OS.CreatePalette (logPalette);
 }
-/**  
+/**
  * Invokes platform specific functionality to allocate a new GC handle.
  * <p>
  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
@@ -741,12 +744,12 @@
  * application code.
  * </p>
  *
- * @param data the platform specific GC data 
+ * @param data the platform specific GC data
  * @return the platform specific GC handle
  */
 public abstract int internal_new_GC (GCData data);
 
-/**  
+/**
  * Invokes platform specific functionality to dispose a GC handle.
  * <p>
  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
@@ -757,7 +760,7 @@
  * </p>
  *
  * @param hDC the platform specific GC handle
- * @param data the platform specific GC data 
+ * @param data the platform specific GC data
  */
 public abstract void internal_dispose_GC (int hDC, GCData data);
 
@@ -788,7 +791,7 @@
  * </ul>
  *
  * @see Font
- * 
+ *
  * @since 3.3
  */
 public bool loadFont (String path) {