root/dwt/graphics/Image.d

Revision 303:eddc0e013cb8, 93.1 kB (checked in by Frank Benoit <benoit@tionex.de>, 4 months ago)

rm trace import

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.graphics.Image;
14
15 import dwt.DWT;
16 import dwt.DWTError;
17 import dwt.DWTException;
18 import dwt.internal.gdip.Gdip;
19 import dwt.internal.win32.OS;
20
21 import dwt.graphics.Color;
22 import dwt.graphics.Device;
23 import dwt.graphics.Drawable;
24 import dwt.graphics.GC;
25 import dwt.graphics.GCData;
26 import dwt.graphics.ImageData;
27 import dwt.graphics.PaletteData;
28 import dwt.graphics.RGB;
29 import dwt.graphics.Rectangle;
30 import dwt.graphics.Resource;
31
32
33 import dwt.dwthelper.InputStream;
34 import dwt.dwthelper.utils;
35
36 import tango.text.convert.Format;
37 //import tango.stdc.string;
38 //import tango.stdc.stringz;
39
40 /**
41  * Instances of this class are graphics which have been prepared
42  * for display on a specific device. That is, they are ready
43  * to paint using methods such as <code>GC.drawImage()</code>
44  * and display on widgets with, for example, <code>Button.setImage()</code>.
45  * <p>
46  * If loaded from a file format that supports it, an
47  * <code>Image</code> may have transparency, meaning that certain
48  * pixels are specified as being transparent when drawn. Examples
49  * of file formats that support transparency are GIF and PNG.
50  * </p><p>
51  * There are two primary ways to use <code>Images</code>.
52  * The first is to load a graphic file from disk and create an
53  * <code>Image</code> from it. This is done using an <code>Image</code>
54  * constructor, for example:
55  * <pre>
56  *    Image i = new Image(device, "C:\\graphic.bmp");
57  * </pre>
58  * A graphic file may contain a color table specifying which
59  * colors the image was intended to possess. In the above example,
60  * these colors will be mapped to the closest available color in
61  * DWT. It is possible to get more control over the mapping of
62  * colors as the image is being created, using code of the form:
63  * <pre>
64  *    ImageData data = new ImageData("C:\\graphic.bmp");
65  *    RGB[] rgbs = data.getRGBs();
66  *    // At this point, rgbs contains specifications of all
67  *    // the colors contained within this image. You may
68  *    // allocate as many of these colors as you wish by
69  *    // using the Color constructor Color(RGB), then
70  *    // create the image:
71  *    Image i = new Image(device, data);
72  * </pre>
73  * <p>
74  * Applications which require even greater control over the image
75  * loading process should use the support provided in class
76  * <code>ImageLoader</code>.
77  * </p><p>
78  * Application code must explicitly invoke the <code>Image.dispose()</code>
79  * method to release the operating system resources managed by each instance
80  * when those instances are no longer required.
81  * </p>
82  *
83  * @see Color
84  * @see ImageData
85  * @see ImageLoader
86  * @see <a href="http://www.eclipse.org/swt/snippets/#image">Image snippets</a>
87  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Examples: GraphicsExample, ImageAnalyzer</a>
88  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
89  */
90
91 public final class Image : Resource, Drawable {
92
93     alias Resource.init_ init_;
94
95     /**
96      * specifies whether the receiver is a bitmap or an icon
97      * (one of <code>DWT.BITMAP</code>, <code>DWT.ICON</code>)
98      * <p>
99      * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
100      * public API. It is marked public only so that it can be shared
101      * within the packages provided by DWT. It is not available on all
102      * platforms and should never be accessed from application code.
103      * </p>
104      */
105     public int type;
106
107     /**
108      * the handle to the OS image resource
109      * (Warning: This field is platform dependent)
110      * <p>
111      * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
112      * public API. It is marked public only so that it can be shared
113      * within the packages provided by DWT. It is not available on all
114      * platforms and should never be accessed from application code.
115      * </p>
116      */
117     public HGDIOBJ handle;
118
119     /**
120      * specifies the transparent pixel
121      */
122     int transparentPixel = -1, transparentColor = -1;
123
124     /**
125      * the GC which is drawing on the image
126      */
127     GC memGC;
128
129     /**
130      * the alpha data for the image
131      */
132     byte[] alphaData;
133
134     /**
135      * the global alpha value to be used for every pixel
136      */
137     int alpha = -1;
138
139     /**
140      * the image data used to create this image if it is a
141      * icon. Used only in WinCE
142      */
143     ImageData data;
144
145     /**
146      * width of the image
147      */
148     int width = -1;
149
150     /**
151      * height of the image
152      */
153     int height = -1;
154
155     /**
156      * specifies the default scanline padding
157      */
158     static const int DEFAULT_SCANLINE_PAD = 4;
159
160 /**
161  * Prevents uninitialized instances from being created outside the package.
162  */
163 this (Device device) {
164     super(device);
165 }
166
167 /**
168  * Constructs an empty instance of this class with the
169  * specified width and height. The result may be drawn upon
170  * by creating a GC and using any of its drawing operations,
171  * as shown in the following example:
172  * <pre>
173  *    Image i = new Image(device, width, height);
174  *    GC gc = new GC(i);
175  *    gc.drawRectangle(0, 0, 50, 50);
176  *    gc.dispose();
177  * </pre>
178  * <p>
179  * Note: Some platforms may have a limitation on the size
180  * of image that can be created (size depends on width, height,
181  * and depth). For example, Windows 95, 98, and ME do not allow
182  * images larger than 16M.
183  * </p>
184  *
185  * @param device the device on which to create the image
186  * @param width the width of the new image
187  * @param height the height of the new image
188  *
189  * @exception IllegalArgumentException <ul>
190  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
191  *    <li>ERROR_INVALID_ARGUMENT - if either the width or height is negative or zero</li>
192  * </ul>
193  * @exception DWTError <ul>
194  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
195  * </ul>
196  */
197 public this(Device device, int width, int height) {
198     super(device);
199     init_(width, height);
200     init_();
201 }
202
203 /**
204  * Constructs a new instance of this class based on the
205  * provided image, with an appearance that varies depending
206  * on the value of the flag. The possible flag values are:
207  * <dl>
208  * <dt><b>{@link DWT#IMAGE_COPY}</b></dt>
209  * <dd>the result is an identical copy of srcImage</dd>
210  * <dt><b>{@link DWT#IMAGE_DISABLE}</b></dt>
211  * <dd>the result is a copy of srcImage which has a <em>disabled</em> look</dd>
212  * <dt><b>{@link DWT#IMAGE_GRAY}</b></dt>
213  * <dd>the result is a copy of srcImage which has a <em>gray scale</em> look</dd>
214  * </dl>
215  *
216  * @param device the device on which to create the image
217  * @param srcImage the image to use as the source
218  * @param flag the style, either <code>IMAGE_COPY</code>, <code>IMAGE_DISABLE</code> or <code>IMAGE_GRAY</code>
219  *
220  * @exception IllegalArgumentException <ul>
221  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
222  *    <li>ERROR_NULL_ARGUMENT - if srcImage is null</li>
223  *    <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>
224  *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
225  * </ul>
226  * @exception DWTException <ul>
227  *    <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon, or is otherwise in an invalid state</li>
228  *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the image is not supported</li>
229  * </ul>
230  * @exception DWTError <ul>
231  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
232  * </ul>
233  */
234 public this(Device device, Image srcImage, int flag) {
235     super(device);
236     device = this.device;
237     if (srcImage is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
238     if (srcImage.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
239     Rectangle rect = srcImage.getBounds();
240     this.type = srcImage.type;
241     switch (flag) {
242         case DWT.IMAGE_COPY: {
243             switch (type) {
244                 case DWT.BITMAP:
245                     /* Get the HDC for the device */
246                     auto hDC = device.internal_new_GC(null);
247
248                     /* Copy the bitmap */
249                     auto hdcSource = OS.CreateCompatibleDC(hDC);
250                     auto hdcDest = OS.CreateCompatibleDC(hDC);
251                     auto hOldSrc = OS.SelectObject(hdcSource, srcImage.handle);
252                     BITMAP bm;
253                     OS.GetObject(srcImage.handle, BITMAP.sizeof, &bm);
254                     handle = OS.CreateCompatibleBitmap(hdcSource, rect.width, bm.bmBits !is null ? -rect.height : rect.height);
255                     if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES);
256                     auto hOldDest = OS.SelectObject(hdcDest, handle);
257                     OS.BitBlt(hdcDest, 0, 0, rect.width, rect.height, hdcSource, 0, 0, OS.SRCCOPY);
258                     OS.SelectObject(hdcSource, hOldSrc);
259                     OS.SelectObject(hdcDest, hOldDest);
260                     OS.DeleteDC(hdcSource);
261                     OS.DeleteDC(hdcDest);
262
263                     /* Release the HDC for the device */
264                     device.internal_dispose_GC(hDC, null);
265
266                     transparentPixel = srcImage.transparentPixel;
267                     alpha = srcImage.alpha;
268                     if (srcImage.alphaData !is null) {
269                         alphaData = new byte[srcImage.alphaData.length];
270                         System.arraycopy(srcImage.alphaData, 0, alphaData, 0, alphaData.length);
271                     }
272                     break;
273                 case DWT.ICON:
274                     static if (OS.IsWinCE) {
275                         init_(srcImage.data);
276                     } else {
277                         handle = OS.CopyImage(srcImage.handle, OS.IMAGE_ICON, rect.width, rect.height, 0);
278                         if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES);
279                     }
280                     break;
281                 default:
282                     DWT.error(DWT.ERROR_INVALID_IMAGE);
283             }
284             break;
285         }
286         case DWT.IMAGE_DISABLE: {
287             ImageData data = srcImage.getImageData();
288             PaletteData palette = data.palette;
289             RGB[] rgbs = new RGB[3];
290             rgbs[0] = device.getSystemColor(DWT.COLOR_BLACK).getRGB();
291             rgbs[1] = device.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW).getRGB();
292             rgbs[2] = device.getSystemColor(DWT.COLOR_WIDGET_BACKGROUND).getRGB();
293             ImageData newData = new ImageData(rect.width, rect.height, 8, new PaletteData(rgbs));
294             newData.alpha = data.alpha;
295             newData.alphaData = data.alphaData;
296             newData.maskData = data.maskData;
297             newData.maskPad = data.maskPad;
298             if (data.transparentPixel !is -1) newData.transparentPixel = 0;
299
300             /* Convert the pixels. */
301             int[] scanline = new int[rect.width];
302             int[] maskScanline = null;
303             ImageData mask = null;
304             if (data.maskData !is null) mask = data.getTransparencyMask();
305             if (mask !is null) maskScanline = new int[rect.width];
306             int redMask = palette.redMask;
307             int greenMask = palette.greenMask;
308             int blueMask = palette.blueMask;
309             int redShift = palette.redShift;
310             int greenShift = palette.greenShift;
311             int blueShift = palette.blueShift;
312             for (int y=0; y<rect.height; y++) {
313                 int offset = y * newData.bytesPerLine;
314                 data.getPixels(0, y, rect.width, scanline, 0);
315                 if (mask !is null) mask.getPixels(0, y, rect.width, maskScanline, 0);
316                 for (int x=0; x<rect.width; x++) {
317                     int pixel = scanline[x];
318                     if (!((data.transparentPixel !is -1 && pixel is data.transparentPixel) || (mask !is null && maskScanline[x] is 0))) {
319                         int red, green, blue;
320                         if (palette.isDirect) {
321                             red = pixel & redMask;
322                             red = (redShift < 0) ? red >>> -redShift : red << redShift;
323                             green = pixel & greenMask;
324                             green = (greenShift < 0) ? green >>> -greenShift : green << greenShift;
325                             blue = pixel & blueMask;
326                             blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift;
327                         } else {
328                             red = palette.colors[pixel].red;
329                             green = palette.colors[pixel].green;
330                             blue = palette.colors[pixel].blue;
331                         }
332                         int intensity = red * red + green * green + blue * blue;
333                         if (intensity < 98304) {
334                             newData.data[offset] = cast(byte)1;
335                         } else {
336                             newData.data[offset] = cast(byte)2;
337                         }
338                     }
339                     offset++;
340                 }
341             }
342             init_ (newData);
343             break;
344         }
345         case DWT.IMAGE_GRAY: {
346             ImageData data = srcImage.getImageData();
347             PaletteData palette = data.palette;
348             ImageData newData = data;
349             if (!palette.isDirect) {
350                 /* Convert the palette entries to gray. */
351                 RGB [] rgbs = palette.getRGBs();
352                 for (int i=0; i<rgbs.length; i++) {
353                     if (data.transparentPixel !is i) {
354                         RGB color = rgbs [i];
355                         int red = color.red;
356                         int green = color.green;
357                         int blue = color.blue;
358                         int intensity = (red+red+green+green+green+green+green+blue) >> 3;
359                         color.red = color.green = color.blue = intensity;
360                     }
361                 }
362                 newData.palette = new PaletteData(rgbs);
363             } else {
364                 /* Create a 8 bit depth image data with a gray palette. */
365                 RGB[] rgbs = new RGB[256];
366                 for (int i=0; i<rgbs.length; i++) {
367                     rgbs[i] = new RGB(i, i, i);
368                 }
369                 newData = new ImageData(rect.width, rect.height, 8, new PaletteData(rgbs));
370                 newData.alpha = data.alpha;
371                 newData.alphaData = data.alphaData;
372                 newData.maskData = data.maskData;
373                 newData.maskPad = data.maskPad;
374                 if (data.transparentPixel !is -1) newData.transparentPixel = 254;
375
376                 /* Convert the pixels. */
377                 int[] scanline = new int[rect.width];
378                 int redMask = palette.redMask;
379                 int greenMask = palette.greenMask;
380                 int blueMask = palette.blueMask;
381                 int redShift = palette.redShift;
382                 int greenShift = palette.greenShift;
383                 int blueShift = palette.blueShift;
384                 for (int y=0; y<rect.height; y++) {
385                     int offset = y * newData.bytesPerLine;
386                     data.getPixels(0, y, rect.width, scanline, 0);
387                     for (int x=0; x<rect.width; x++) {
388                         int pixel = scanline[x];
389                         if (pixel !is data.transparentPixel) {
390                             int red = pixel & redMask;
391                             red = (redShift < 0) ? red >>> -redShift : red << redShift;
392                             int green = pixel & greenMask;
393                             green = (greenShift < 0) ? green >>> -greenShift : green << greenShift;
394                             int blue = pixel & blueMask;
395                             blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift;
396                             int intensity = (red+red+green+green+green+green+green+blue) >> 3;
397                             if (newData.transparentPixel is intensity) intensity = 255;
398                             newData.data[offset] = cast(byte)intensity;
399                         } else {
400                             newData.data[offset] = cast(byte)254;
401                         }
402                         offset++;
403                     }
404                 }
405             }
406             init_ (newData);
407             break;
408         }
409         default:
410             DWT.error(DWT.ERROR_INVALID_ARGUMENT);
411     }
412     init_();
413 }
414
415 /**
416  * Constructs an empty instance of this class with the
417  * width and height of the specified rectangle. The result
418  * may be drawn upon by creating a GC and using any of its
419  * drawing operations, as shown in the following example:
420  * <pre>
421  *    Image i = new Image(device, boundsRectangle);
422  *    GC gc = new GC(i);
423  *    gc.drawRectangle(0, 0, 50, 50);
424  *    gc.dispose();
425  * </pre>
426  * <p>
427  * Note: Some platforms may have a limitation on the size
428  * of image that can be created (size depends on width, height,
429  * and depth). For example, Windows 95, 98, and ME do not allow
430  * images larger than 16M.
431  * </p>
432  *
433  * @param device the device on which to create the image
434  * @param bounds a rectangle specifying the image's width and height (must not be null)
435  *
436  * @exception IllegalArgumentException <ul>
437  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
438  *    <li>ERROR_NULL_ARGUMENT - if the bounds rectangle is null</li>
439  *    <li>ERROR_INVALID_ARGUMENT - if either the rectangle's width or height is negative</li>
440  * </ul>
441  * @exception DWTError <ul>
442  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
443  * </ul>
444  */
445 public this(Device device, Rectangle bounds) {
446     super(device);
447     if (bounds is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
448     init_(bounds.width, bounds.height);
449     init_();
450 }
451
452 /**
453  * Constructs an instance of this class from the given
454  * <code>ImageData</code>.
455  *
456  * @param device the device on which to create the image
457  * @param data the image data to create the image from (must not be null)
458  *
459  * @exception IllegalArgumentException <ul>
460  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
461  *    <li>ERROR_NULL_ARGUMENT - if the image data is null</li>
462  * </ul>
463  * @exception DWTException <ul>
464  *    <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the ImageData is not supported</li>
465  * </ul>
466  * @exception DWTError <ul>
467  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
468  * </ul>
469  */
470 public this(Device device, ImageData data) {
471     super(device);
472     init_(data);
473     init_();
474 }
475
476 /**
477  * Constructs an instance of this class, whose type is
478  * <code>DWT.ICON</code>, from the two given <code>ImageData</code>
479  * objects. The two images must be the same size. Pixel transparency
480  * in either image will be ignored.
481  * <p>
482  * The mask image should contain white wherever the icon is to be visible,
483  * and black wherever the icon is to be transparent. In addition,
484  * the source image should contain black wherever the icon is to be
485  * transparent.
486  * </p>
487  *
488  * @param device the device on which to create the icon
489  * @param source the color data for the icon
490  * @param mask the mask data for the icon
491  *
492  * @exception IllegalArgumentException <ul>
493  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
494  *    <li>ERROR_NULL_ARGUMENT - if either the source or mask is null </li>
495  *    <li>ERROR_INVALID_ARGUMENT - if source and mask are different sizes</li>
496  * </ul>
497  * @exception DWTError <ul>
498  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
499  * </ul>
500  */
501 public this(Device device, ImageData source, ImageData mask) {
502     super(device);
503     if (source is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
504     if (mask is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
505     if (source.width !is mask.width || source.height !is mask.height) {
506         DWT.error(DWT.ERROR_INVALID_ARGUMENT);
507     }
508     mask = ImageData.convertMask(mask);
509     init_(this.device, this, source, mask);
510     init_();
511 }
512
513 /**
514  * Constructs an instance of this class by loading its representation
515  * from the specified input stream. Throws an error if an error
516  * occurs while loading the image, or if the result is an image
517  * of an unsupported type.  Application code is still responsible
518  * for closing the input stream.
519  * <p>
520  * This constructor is provided for convenience when loading a single
521  * image only. If the stream contains multiple images, only the first
522  * one will be loaded. To load multiple images, use
523  * <code>ImageLoader.load()</code>.
524  * </p><p>
525  * This constructor may be used to load a resource as follows:
526  * </p>
527  * <pre>
528  *     static Image loadImage (Display display, Class clazz, String string) {
529  *          InputStream stream = clazz.getResourceAsStream (string);
530  *          if (stream is null) return null;
531  *          Image image = null;
532  *          try {
533  *               image = new Image (display, stream);
534  *          } catch (DWTException ex) {
535  *          } finally {
536  *               try {
537  *                    stream.close ();
538  *               } catch (IOException ex) {}
539  *          }
540  *          return image;
541  *     }
542  * </pre>
543  *
544  * @param device the device on which to create the image
545  * @param stream the input stream to load the image from
546  *
547  * @exception IllegalArgumentException <ul>
548  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
549  *    <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
550  * </ul>
551  * @exception DWTException <ul>
552  *    <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
553  *    <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data </li>
554  *    <li>ERROR_UNSUPPORTED_DEPTH - if the image stream describes an image with an unsupported depth</li>
555  *    <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
556  * </ul>
557  * @exception DWTError <ul>
558  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
559  * </ul>
560  */
561 public this (Device device, InputStream stream) {
562     super(device);
563     init_(new ImageData(stream));
564     init_();
565 }
566
567 /**
568  * Constructs an instance of this class by loading its representation
569  * from the file with the specified name. Throws an error if an error
570  * occurs while loading the image, or if the result is an image
571  * of an unsupported type.
572  * <p>
573  * This constructor is provided for convenience when loading
574  * a single image only. If the specified file contains
575  * multiple images, only the first one will be used.
576  *
577  * @param device the device on which to create the image
578  * @param filename the name of the file to load the image from
579  *
580  * @exception IllegalArgumentException <ul>
581  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
582  *    <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
583  * </ul>
584  * @exception DWTException <ul>
585  *    <li>ERROR_IO - if an IO error occurs while reading from the file</li>
586  *    <li>ERROR_INVALID_IMAGE - if the image file contains invalid data </li>
587  *    <li>ERROR_UNSUPPORTED_DEPTH - if the image file describes an image with an unsupported depth</li>
588  *    <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
589  * </ul>
590  * @exception DWTError <ul>
591  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
592  * </ul>
593  */
594 public this (Device device, String filename) {
595     super(device);
596     device = this.device;
597     if (filename is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
598     bool gdip = true;
599     try {
600         device.