comparison dwt/graphics/Image.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children 649b8e223d5a
comparison
equal deleted inserted replaced
-1:000000000000 0:380af2bdd8e5
1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 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 *******************************************************************************/
11 module dwt.graphics.Image;
12
13 import dwt.dwthelper.utils;
14
15
16 import java.io.InputStream;
17
18 import dwt.DWT;
19 import dwt.DWTError;
20 import dwt.DWTException;
21 import dwt.internal.cocoa.NSAffineTransform;
22 import dwt.internal.cocoa.NSBitmapImageRep;
23 import dwt.internal.cocoa.NSGraphicsContext;
24 import dwt.internal.cocoa.NSImage;
25 import dwt.internal.cocoa.NSImageRep;
26 import dwt.internal.cocoa.NSSize;
27 import dwt.internal.cocoa.NSString;
28 import dwt.internal.cocoa.OS;
29
30 /**
31 * Instances of this class are graphics which have been prepared
32 * for display on a specific device. That is, they are ready
33 * to paint using methods such as <code>GC.drawImage()</code>
34 * and display on widgets with, for example, <code>Button.setImage()</code>.
35 * <p>
36 * If loaded from a file format that supports it, an
37 * <code>Image</code> may have transparency, meaning that certain
38 * pixels are specified as being transparent when drawn. Examples
39 * of file formats that support transparency are GIF and PNG.
40 * </p><p>
41 * There are two primary ways to use <code>Images</code>.
42 * The first is to load a graphic file from disk and create an
43 * <code>Image</code> from it. This is done using an <code>Image</code>
44 * constructor, for example:
45 * <pre>
46 * Image i = new Image(device, "C:\\graphic.bmp");
47 * </pre>
48 * A graphic file may contain a color table specifying which
49 * colors the image was intended to possess. In the above example,
50 * these colors will be mapped to the closest available color in
51 * DWT. It is possible to get more control over the mapping of
52 * colors as the image is being created, using code of the form:
53 * <pre>
54 * ImageData data = new ImageData("C:\\graphic.bmp");
55 * RGB[] rgbs = data.getRGBs();
56 * // At this point, rgbs contains specifications of all
57 * // the colors contained within this image. You may
58 * // allocate as many of these colors as you wish by
59 * // using the Color constructor Color(RGB), then
60 * // create the image:
61 * Image i = new Image(device, data);
62 * </pre>
63 * <p>
64 * Applications which require even greater control over the image
65 * loading process should use the support provided in class
66 * <code>ImageLoader</code>.
67 * </p><p>
68 * Application code must explicitly invoke the <code>Image.dispose()</code>
69 * method to release the operating system resources managed by each instance
70 * when those instances are no longer required.
71 * </p>
72 *
73 * @see Color
74 * @see ImageData
75 * @see ImageLoader
76 */
77 public final class Image extends Resource implements Drawable {
78
79 /**
80 * specifies whether the receiver is a bitmap or an icon
81 * (one of <code>DWT.BITMAP</code>, <code>DWT.ICON</code>)
82 * <p>
83 * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
84 * public API. It is marked public only so that it can be shared
85 * within the packages provided by DWT. It is not available on all
86 * platforms and should never be accessed from application code.
87 * </p>
88 */
89 public int type;
90
91 /**
92 * the handle to the OS image resource
93 * (Warning: This field is platform dependent)
94 * <p>
95 * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
96 * public API. It is marked public only so that it can be shared
97 * within the packages provided by DWT. It is not available on all
98 * platforms and should never be accessed from application code.
99 * </p>
100 */
101 public NSImage handle;
102 NSBitmapImageRep imageRep;
103
104 /**
105 * specifies the transparent pixel
106 */
107 int transparentPixel = -1;
108
109 /**
110 * The GC the image is currently selected in.
111 */
112 GC memGC;
113
114 /**
115 * The alpha data of the image.
116 */
117 byte[] alphaData;
118
119 /**
120 * The global alpha value to be used for every pixel.
121 */
122 int alpha = -1;
123
124 /**
125 * The width of the image.
126 */
127 int width = -1;
128
129 /**
130 * The height of the image.
131 */
132 int height = -1;
133
134 /**
135 * Specifies the default scanline padding.
136 */
137 static final int DEFAULT_SCANLINE_PAD = 4;
138
139 Image(Device device) {
140 super(device);
141 }
142
143 /**
144 * Constructs an empty instance of this class with the
145 * specified width and height. The result may be drawn upon
146 * by creating a GC and using any of its drawing operations,
147 * as shown in the following example:
148 * <pre>
149 * Image i = new Image(device, width, height);
150 * GC gc = new GC(i);
151 * gc.drawRectangle(0, 0, 50, 50);
152 * gc.dispose();
153 * </pre>
154 * <p>
155 * Note: Some platforms may have a limitation on the size
156 * of image that can be created (size depends on width, height,
157 * and depth). For example, Windows 95, 98, and ME do not allow
158 * images larger than 16M.
159 * </p>
160 *
161 * @param device the device on which to create the image
162 * @param width the width of the new image
163 * @param height the height of the new image
164 *
165 * @exception IllegalArgumentException <ul>
166 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
167 * <li>ERROR_INVALID_ARGUMENT - if either the width or height is negative or zero</li>
168 * </ul>
169 * @exception DWTError <ul>
170 * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
171 * </ul>
172 */
173 public Image(Device device, int width, int height) {
174 super(device);
175 init(width, height);
176 init();
177 }
178
179 /**
180 * Constructs a new instance of this class based on the
181 * provided image, with an appearance that varies depending
182 * on the value of the flag. The possible flag values are:
183 * <dl>
184 * <dt><b>IMAGE_COPY</b></dt>
185 * <dd>the result is an identical copy of srcImage</dd>
186 * <dt><b>IMAGE_DISABLE</b></dt>
187 * <dd>the result is a copy of srcImage which has a <em>disabled</em> look</dd>
188 * <dt><b>IMAGE_GRAY</b></dt>
189 * <dd>the result is a copy of srcImage which has a <em>gray scale</em> look</dd>
190 * </dl>
191 *
192 * @param device the device on which to create the image
193 * @param srcImage the image to use as the source
194 * @param flag the style, either <code>IMAGE_COPY</code>, <code>IMAGE_DISABLE</code> or <code>IMAGE_GRAY</code>
195 *
196 * @exception IllegalArgumentException <ul>
197 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
198 * <li>ERROR_NULL_ARGUMENT - if srcImage is null</li>
199 * <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>
200 * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
201 * </ul>
202 * @exception DWTException <ul>
203 * <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon, or is otherwise in an invalid state</li>
204 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the image is not supported</li>
205 * </ul>
206 * @exception DWTError <ul>
207 * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
208 * </ul>
209 */
210 public Image(Device device, Image srcImage, int flag) {
211 super(device);
212 if (srcImage is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
213 if (srcImage.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
214 switch (flag) {
215 case DWT.IMAGE_COPY:
216 case DWT.IMAGE_DISABLE:
217 case DWT.IMAGE_GRAY:
218 break;
219 default:
220 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
221 }
222 device = this.device;
223 this.type = srcImage.type;
224
225 /* Get source image size */
226 NSSize size = srcImage.handle.size();
227 int width = (int)size.width;
228 int height = (int)size.height;
229 NSBitmapImageRep srcRep = srcImage.imageRep;
230 int bpr = srcRep.bytesPerRow();
231
232 /* Copy transparent pixel and alpha data when necessary */
233 transparentPixel = srcImage.transparentPixel;
234 alpha = srcImage.alpha;
235 if (srcImage.alphaData !is null) {
236 alphaData = new byte[srcImage.alphaData.length];
237 System.arraycopy(srcImage.alphaData, 0, alphaData, 0, alphaData.length);
238 }
239
240 /* Create the image */
241 handle = (NSImage)new NSImage().alloc();
242 handle = handle.initWithSize(size);
243 NSBitmapImageRep rep = imageRep = (NSBitmapImageRep)new NSBitmapImageRep().alloc();
244 rep = rep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_(0, width, height, srcRep.bitsPerSample(), srcRep.samplesPerPixel(), srcRep.samplesPerPixel() is 4, srcRep.isPlanar(), new NSString(OS.NSDeviceRGBColorSpace()), OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, srcRep.bytesPerRow(), srcRep.bitsPerPixel());
245 handle.addRepresentation(rep);
246
247 int data = rep.bitmapData();
248 OS.memmove(data, srcImage.imageRep.bitmapData(), width * height * 4);
249 if (flag !is DWT.IMAGE_COPY) {
250
251 /* Apply transformation */
252 switch (flag) {
253 case DWT.IMAGE_DISABLE: {
254 Color zeroColor = device.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
255 RGB zeroRGB = zeroColor.getRGB();
256 byte zeroRed = (byte)zeroRGB.red;
257 byte zeroGreen = (byte)zeroRGB.green;
258 byte zeroBlue = (byte)zeroRGB.blue;
259 Color oneColor = device.getSystemColor(DWT.COLOR_WIDGET_BACKGROUND);
260 RGB oneRGB = oneColor.getRGB();
261 byte oneRed = (byte)oneRGB.red;
262 byte oneGreen = (byte)oneRGB.green;
263 byte oneBlue = (byte)oneRGB.blue;
264 byte[] line = new byte[bpr];
265 for (int y=0; y<height; y++) {
266 OS.memmove(line, data + (y * bpr), bpr);
267 int offset = 0;
268 for (int x=0; x<width; x++) {
269 int red = line[offset+1] & 0xFF;
270 int green = line[offset+2] & 0xFF;
271 int blue = line[offset+3] & 0xFF;
272 int intensity = red * red + green * green + blue * blue;
273 if (intensity < 98304) {
274 line[offset+1] = zeroRed;
275 line[offset+2] = zeroGreen;
276 line[offset+3] = zeroBlue;
277 } else {
278 line[offset+1] = oneRed;
279 line[offset+2] = oneGreen;
280 line[offset+3] = oneBlue;
281 }
282 offset += 4;
283 }
284 OS.memmove(data + (y * bpr), line, bpr);
285 }
286 break;
287 }
288 case DWT.IMAGE_GRAY: {
289 byte[] line = new byte[bpr];
290 for (int y=0; y<height; y++) {
291 OS.memmove(line, data + (y * bpr), bpr);
292 int offset = 0;
293 for (int x=0; x<width; x++) {
294 int red = line[offset+1] & 0xFF;
295 int green = line[offset+2] & 0xFF;
296 int blue = line[offset+3] & 0xFF;
297 byte intensity = (byte)((red+red+green+green+green+green+green+blue) >> 3);
298 line[offset+1] = line[offset+2] = line[offset+3] = intensity;
299 offset += 4;
300 }
301 OS.memmove(data + (y * bpr), line, bpr);
302 }
303 break;
304 }
305 }
306 }
307 init();
308 }
309
310 /**
311 * Constructs an empty instance of this class with the
312 * width and height of the specified rectangle. The result
313 * may be drawn upon by creating a GC and using any of its
314 * drawing operations, as shown in the following example:
315 * <pre>
316 * Image i = new Image(device, boundsRectangle);
317 * GC gc = new GC(i);
318 * gc.drawRectangle(0, 0, 50, 50);
319 * gc.dispose();
320 * </pre>
321 * <p>
322 * Note: Some platforms may have a limitation on the size
323 * of image that can be created (size depends on width, height,
324 * and depth). For example, Windows 95, 98, and ME do not allow
325 * images larger than 16M.
326 * </p>
327 *
328 * @param device the device on which to create the image
329 * @param bounds a rectangle specifying the image's width and height (must not be null)
330 *
331 * @exception IllegalArgumentException <ul>
332 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
333 * <li>ERROR_NULL_ARGUMENT - if the bounds rectangle is null</li>
334 * <li>ERROR_INVALID_ARGUMENT - if either the rectangle's width or height is negative</li>
335 * </ul>
336 * @exception DWTError <ul>
337 * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
338 * </ul>
339 */
340 public Image(Device device, Rectangle bounds) {
341 super(device);
342 if (bounds is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
343 init(bounds.width, bounds.height);
344 init();
345 }
346
347 /**
348 * Constructs an instance of this class from the given
349 * <code>ImageData</code>.
350 *
351 * @param device the device on which to create the image
352 * @param data the image data to create the image from (must not be null)
353 *
354 * @exception IllegalArgumentException <ul>
355 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
356 * <li>ERROR_NULL_ARGUMENT - if the image data is null</li>
357 * </ul>
358 * @exception DWTException <ul>
359 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth of the ImageData is not supported</li>
360 * </ul>
361 * @exception DWTError <ul>
362 * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
363 * </ul>
364 */
365 public Image(Device device, ImageData data) {
366 super(device);
367 init(data);
368 init();
369 }
370
371 /**
372 * Constructs an instance of this class, whose type is
373 * <code>DWT.ICON</code>, from the two given <code>ImageData</code>
374 * objects. The two images must be the same size. Pixel transparency
375 * in either image will be ignored.
376 * <p>
377 * The mask image should contain white wherever the icon is to be visible,
378 * and black wherever the icon is to be transparent. In addition,
379 * the source image should contain black wherever the icon is to be
380 * transparent.
381 * </p>
382 *
383 * @param device the device on which to create the icon
384 * @param source the color data for the icon
385 * @param mask the mask data for the icon
386 *
387 * @exception IllegalArgumentException <ul>
388 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
389 * <li>ERROR_NULL_ARGUMENT - if either the source or mask is null </li>
390 * <li>ERROR_INVALID_ARGUMENT - if source and mask are different sizes</li>
391 * </ul>
392 * @exception DWTError <ul>
393 * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
394 * </ul>
395 */
396 public Image(Device device, ImageData source, ImageData mask) {
397 super(device);
398 if (source is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
399 if (mask is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
400 if (source.width !is mask.width || source.height !is mask.height) {
401 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
402 }
403 mask = ImageData.convertMask(mask);
404 ImageData image = new ImageData(source.width, source.height, source.depth, source.palette, source.scanlinePad, source.data);
405 image.maskPad = mask.scanlinePad;
406 image.maskData = mask.data;
407 init(image);
408 }
409
410 /**
411 * Constructs an instance of this class by loading its representation
412 * from the specified input stream. Throws an error if an error
413 * occurs while loading the image, or if the result is an image
414 * of an unsupported type. Application code is still responsible
415 * for closing the input stream.
416 * <p>
417 * This constructor is provided for convenience when loading a single
418 * image only. If the stream contains multiple images, only the first
419 * one will be loaded. To load multiple images, use
420 * <code>ImageLoader.load()</code>.
421 * </p><p>
422 * This constructor may be used to load a resource as follows:
423 * </p>
424 * <pre>
425 * static Image loadImage (Display display, Class clazz, String string) {
426 * InputStream stream = clazz.getResourceAsStream (string);
427 * if (stream is null) return null;
428 * Image image = null;
429 * try {
430 * image = new Image (display, stream);
431 * } catch (DWTException ex) {
432 * } finally {
433 * try {
434 * stream.close ();
435 * } catch (IOException ex) {}
436 * }
437 * return image;
438 * }
439 * </pre>
440 *
441 * @param device the device on which to create the image
442 * @param stream the input stream to load the image from
443 *
444 * @exception IllegalArgumentException <ul>
445 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
446 * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
447 * </ul>
448 * @exception DWTException <ul>
449 * <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
450 * <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data </li>
451 * <li>ERROR_UNSUPPORTED_DEPTH - if the image stream describes an image with an unsupported depth</li>
452 * <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
453 * </ul>
454 * @exception DWTError <ul>
455 * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
456 * </ul>
457 */
458 public Image(Device device, InputStream stream) {
459 super(device);
460 init(new ImageData(stream));
461 init();
462 }
463
464 /**
465 * Constructs an instance of this class by loading its representation
466 * from the file with the specified name. Throws an error if an error
467 * occurs while loading the image, or if the result is an image
468 * of an unsupported type.
469 * <p>
470 * This constructor is provided for convenience when loading
471 * a single image only. If the specified file contains
472 * multiple images, only the first one will be used.
473 *
474 * @param device the device on which to create the image
475 * @param filename the name of the file to load the image from
476 *
477 * @exception IllegalArgumentException <ul>
478 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
479 * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
480 * </ul>
481 * @exception DWTException <ul>
482 * <li>ERROR_IO - if an IO error occurs while reading from the file</li>
483 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data </li>
484 * <li>ERROR_UNSUPPORTED_DEPTH - if the image file describes an image with an unsupported depth</li>
485 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
486 * </ul>
487 * @exception DWTError <ul>
488 * <li>ERROR_NO_HANDLES if a handle could not be obtained for image creation</li>
489 * </ul>
490 */
491 public Image(Device device, String filename) {
492 super(device);
493 init(new ImageData(filename));
494 init();
495 }
496
497 void createAlpha () {
498 if (transparentPixel is -1 && alpha is -1 && alphaData is null) return;
499 NSSize size = handle.size();
500 int height = (int)size.height;
501 int bpr = imageRep.bytesPerRow();
502 int dataSize = height * bpr;
503 byte[] srcData = new byte[dataSize];
504 OS.memmove(srcData, imageRep.bitmapData(), dataSize);
505 if (transparentPixel !is -1) {
506 for (int i=0; i<dataSize; i+=4) {
507 int pixel = ((srcData[i+1] & 0xFF) << 16) | ((srcData[i+2] & 0xFF) << 8) | (srcData[i+3] & 0xFF);
508 srcData[i] = (byte)(pixel is transparentPixel ? 0 : 0xFF);
509 }
510 } else if (alpha !is -1) {
511 byte a = (byte)this.alpha;
512 for (int i=0; i<dataSize; i+=4) {
513 srcData[i] = a;
514 }
515 } else {
516 int width = (int)size.width;
517 int offset = 0, alphaOffset = 0;
518 for (int y = 0; y<height; y++) {
519 for (int x = 0; x<width; x++) {
520 srcData[offset] = alphaData[alphaOffset];
521 offset += 4;
522 alphaOffset += 1;
523 }
524 }
525 }
526 OS.memmove(imageRep.bitmapData(), srcData, dataSize);
527 }
528
529 void destroy() {
530 if (memGC !is null) memGC.dispose();
531 if (imageRep !is null) imageRep.release();
532 handle.release();
533 imageRep = null;
534 handle = null;
535 memGC = null;
536 }
537
538 /**
539 * Compares the argument to the receiver, and returns true
540 * if they represent the <em>same</em> object using a class
541 * specific comparison.
542 *
543 * @param object the object to compare with this object
544 * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
545 *
546 * @see #hashCode
547 */
548 public bool equals (Object object) {
549 if (object is this) return true;
550 if (!(object instanceof Image)) return false;
551 Image image = (Image)object;
552 return device is image.device && handle is image.handle &&
553 transparentPixel is image.transparentPixel;
554 }
555
556 /**
557 * Returns the color to which to map the transparent pixel, or null if
558 * the receiver has no transparent pixel.
559 * <p>
560 * There are certain uses of Images that do not support transparency
561 * (for example, setting an image into a button or label). In these cases,
562 * it may be desired to simulate transparency by using the background
563 * color of the widget to paint the transparent pixels of the image.
564 * Use this method to check which color will be used in these cases
565 * in place of transparency. This value may be set with setBackground().
566 * <p>
567 *
568 * @return the background color of the image, or null if there is no transparency in the image
569 *
570 * @exception DWTException <ul>
571 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
572 * </ul>
573 */
574 public Color getBackground() {
575 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
576 if (transparentPixel is -1) return null;
577 int red = (transparentPixel >> 16) & 0xFF;
578 int green = (transparentPixel >> 8) & 0xFF;
579 int blue = (transparentPixel >> 0) & 0xFF;
580 return Color.cocoa_new(device, new float[]{red / 255f, green / 255f, blue / 255f, 1});
581 }
582
583 /**
584 * Returns the bounds of the receiver. The rectangle will always
585 * have x and y values of 0, and the width and height of the
586 * image.
587 *
588 * @return a rectangle specifying the image's bounds
589 *
590 * @exception DWTException <ul>
591 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
592 * <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li>
593 * </ul>
594 */
595 public Rectangle getBounds() {
596 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
597 if (width !is -1 && height !is -1) {
598 return new Rectangle(0, 0, width, height);
599 }
600 NSSize size = handle.size();
601 return new Rectangle(0, 0, width = (int)size.width, height = (int)size.height);
602 }
603
604 /**
605 * Returns an <code>ImageData</code> based on the receiver
606 * Modifications made to this <code>ImageData</code> will not
607 * affect the Image.
608 *
609 * @return an <code>ImageData</code> containing the image's data and attributes
610 *
611 * @exception DWTException <ul>
612 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
613 * <li>ERROR_INVALID_IMAGE - if the image is not a bitmap or an icon</li>
614 * </ul>
615 *
616 * @see ImageData
617 */
618 public ImageData getImageData() {
619 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
620
621 NSSize size = handle.size();
622 int width = (int)size.width;
623 int height = (int)size.height;
624 NSBitmapImageRep imageRep = this.imageRep;
625 int bpr = imageRep.bytesPerRow();
626 int bpp = imageRep.bitsPerPixel();
627 int dataSize = height * bpr;
628 byte[] srcData = new byte[dataSize];
629 OS.memmove(srcData, imageRep.bitmapData(), dataSize);
630
631 PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF);
632 ImageData data = new ImageData(width, height, bpp, palette);
633 data.data = srcData;
634 data.bytesPerLine = bpr;
635
636 data.transparentPixel = transparentPixel;
637 if (transparentPixel is -1 && type is DWT.ICON) {
638 /* Get the icon mask data */
639 int maskPad = 2;
640 int maskBpl = (((width + 7) / 8) + (maskPad - 1)) / maskPad * maskPad;
641 byte[] maskData = new byte[height * maskBpl];
642 int offset = 0, maskOffset = 0;
643 for (int y = 0; y<height; y++) {
644 for (int x = 0; x<width; x++) {
645 if (srcData[offset] !is 0) {
646 maskData[maskOffset + (x >> 3)] |= (1 << (7 - (x & 0x7)));
647 } else {
648 maskData[maskOffset + (x >> 3)] &= ~(1 << (7 - (x & 0x7)));
649 }
650 offset += 4;
651 }
652 maskOffset += maskBpl;
653 }
654 data.maskData = maskData;
655 data.maskPad = maskPad;
656 }
657 for (int i = 0; i < srcData.length; i+= 4) {
658 srcData[i] = 0;
659 }
660 data.alpha = alpha;
661 if (alpha is -1 && alphaData !is null) {
662 data.alphaData = new byte[alphaData.length];
663 System.arraycopy(alphaData, 0, data.alphaData, 0, alphaData.length);
664 }
665 return data;
666 }
667
668 /**
669 * Invokes platform specific functionality to allocate a new image.
670 * <p>
671 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
672 * API for <code>Image</code>. It is marked public only so that it
673 * can be shared within the packages provided by DWT. It is not
674 * available on all platforms, and should never be called from
675 * application code.
676 * </p>
677 *
678 * @param device the device on which to allocate the color
679 * @param type the type of the image (<code>DWT.BITMAP</code> or <code>DWT.ICON</code>)
680 * @param handle the OS handle for the image
681 * @param data the OS data for the image
682 *
683 * @private
684 */
685 public static Image cocoa_new(Device device, int type, NSImage nsImage) {
686 Image image = new Image(device);
687 image.type = type;
688 image.handle = nsImage;
689 NSImageRep rep = nsImage.bestRepresentationForDevice(null);
690 if (rep.isKindOfClass(NSBitmapImageRep.static_class())) {
691 image.imageRep = new NSBitmapImageRep(rep.id);
692 }
693 return image;
694 }
695
696 /**
697 * Returns an integer hash code for the receiver. Any two
698 * objects that return <code>true</code> when passed to
699 * <code>equals</code> must return the same value for this
700 * method.
701 *
702 * @return the receiver's hash
703 *
704 * @see #equals
705 */
706 public int hashCode () {
707 return handle !is null ? handle.id : 0;
708 }
709
710 void init(int width, int height) {
711 if (width <= 0 || height <= 0) {
712 DWT.error (DWT.ERROR_INVALID_ARGUMENT);
713 }
714 this.type = DWT.BITMAP;
715 this.width = width;
716 this.height = height;
717
718 handle = (NSImage)new NSImage().alloc();
719 NSSize size = new NSSize();
720 size.width = width;
721 size.height = height;
722 handle = handle.initWithSize(size);
723 NSBitmapImageRep rep = imageRep = (NSBitmapImageRep)new NSBitmapImageRep().alloc();
724 rep = rep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_(0, width, height, 8, 3, false, false, new NSString(OS.NSDeviceRGBColorSpace()), OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, width * 4, 32);
725 OS.memset(rep.bitmapData(), 0xFF, width * height * 4);
726 handle.addRepresentation(rep);
727 // rep.release();
728 }
729
730 void init(ImageData image) {
731 if (image is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
732 this.width = image.width;
733 this.height = image.height;
734 PaletteData palette = image.palette;
735 if (!(((image.depth is 1 || image.depth is 2 || image.depth is 4 || image.depth is 8) && !palette.isDirect) ||
736 ((image.depth is 8) || (image.depth is 16 || image.depth is 24 || image.depth is 32) && palette.isDirect)))
737 DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
738
739 /* Create the image */
740 int dataSize = width * height * 4;
741
742 /* Initialize data */
743 int bpr = width * 4;
744 byte[] buffer = new byte[dataSize];
745 if (palette.isDirect) {
746 ImageData.blit(ImageData.BLIT_SRC,
747 image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, palette.redMask, palette.greenMask, palette.blueMask,
748 ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
749 buffer, 32, bpr, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF,
750 false, false);
751 } else {
752 RGB[] rgbs = palette.getRGBs();
753 int length = rgbs.length;
754 byte[] srcReds = new byte[length];
755 byte[] srcGreens = new byte[length];
756 byte[] srcBlues = new byte[length];
757 for (int i = 0; i < rgbs.length; i++) {
758 RGB rgb = rgbs[i];
759 if (rgb is null) continue;
760 srcReds[i] = (byte)rgb.red;
761 srcGreens[i] = (byte)rgb.green;
762 srcBlues[i] = (byte)rgb.blue;
763 }
764 ImageData.blit(ImageData.BLIT_SRC,
765 image.data, image.depth, image.bytesPerLine, image.getByteOrder(), 0, 0, width, height, srcReds, srcGreens, srcBlues,
766 ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
767 buffer, 32, bpr, ImageData.MSB_FIRST, 0, 0, width, height, 0xFF0000, 0xFF00, 0xFF,
768 false, false);
769 }
770
771 /* Initialize transparency */
772 int transparency = image.getTransparencyType();
773 bool hasAlpha = transparency !is DWT.TRANSPARENCY_NONE;
774 if (transparency is DWT.TRANSPARENCY_MASK || image.transparentPixel !is -1) {
775 this.type = image.transparentPixel !is -1 ? DWT.BITMAP : DWT.ICON;
776 if (image.transparentPixel !is -1) {
777 int transRed = 0, transGreen = 0, transBlue = 0;
778 if (palette.isDirect) {
779 RGB rgb = palette.getRGB(image.transparentPixel);
780 transRed = rgb.red;
781 transGreen = rgb.green;
782 transBlue = rgb.blue;
783 } else {
784 RGB[] rgbs = palette.getRGBs();
785 if (image.transparentPixel < rgbs.length) {
786 RGB rgb = rgbs[image.transparentPixel];
787 transRed = rgb.red;
788 transGreen = rgb.green;
789 transBlue = rgb.blue;
790 }
791 }
792 transparentPixel = transRed << 16 | transGreen << 8 | transBlue;
793 }
794 ImageData maskImage = image.getTransparencyMask();
795 byte[] maskData = maskImage.data;
796 int maskBpl = maskImage.bytesPerLine;
797 int offset = 0, maskOffset = 0;
798 for (int y = 0; y<height; y++) {
799 for (int x = 0; x<width; x++) {
800 buffer[offset] = ((maskData[maskOffset + (x >> 3)]) & (1 << (7 - (x & 0x7)))) !is 0 ? (byte)0xff : 0;
801 offset += 4;
802 }
803 maskOffset += maskBpl;
804 }
805 } else {
806 this.type = DWT.BITMAP;
807 if (image.alpha !is -1) {
808 hasAlpha = true;
809 this.alpha = image.alpha;
810 byte a = (byte)this.alpha;
811 for (int dataIndex=0; dataIndex<buffer.length; dataIndex+=4) {
812 buffer[dataIndex] = a;
813 }
814 } else if (image.alphaData !is null) {
815 this.alphaData = new byte[image.alphaData.length];
816 System.arraycopy(image.alphaData, 0, this.alphaData, 0, alphaData.length);
817 int offset = 0, alphaOffset = 0;
818 for (int y = 0; y<height; y++) {
819 for (int x = 0; x<width; x++) {
820 buffer[offset] = alphaData[alphaOffset];
821 offset += 4;
822 alphaOffset += 1;
823 }
824 }
825 }
826 }
827 handle = (NSImage)new NSImage().alloc();
828 NSSize size = new NSSize();
829 size.width = width;
830 size.height = height;
831 handle = handle.initWithSize(size);
832 NSBitmapImageRep rep = imageRep = (NSBitmapImageRep)new NSBitmapImageRep().alloc();
833 rep = rep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_(
834 0, width, height, 8, hasAlpha ? 4 : 3, hasAlpha, false, new NSString(OS.NSDeviceRGBColorSpace()), OS.NSAlphaFirstBitmapFormat | OS.NSAlphaNonpremultipliedBitmapFormat, bpr, 32);
835 OS.memmove(rep.bitmapData(), buffer, dataSize);
836 handle.addRepresentation(rep);
837 // rep.release();
838 }
839
840 /**
841 * Invokes platform specific functionality to allocate a new GC handle.
842 * <p>
843 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
844 * API for <code>Image</code>. It is marked public only so that it
845 * can be shared within the packages provided by DWT. It is not
846 * available on all platforms, and should never be called from
847 * application code.
848 * </p>
849 *
850 * @param data the platform specific GC data
851 * @return the platform specific GC handle
852 */
853 public int internal_new_GC (GCData data) {
854 if (handle is null) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
855 if (type !is DWT.BITMAP || memGC !is null) {
856 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
857 }
858 NSGraphicsContext current = NSGraphicsContext.currentContext();
859 NSBitmapImageRep rep = imageRep;
860 if (imageRep.hasAlpha()) {
861 int bpr = width * 4;
862 rep = (NSBitmapImageRep)new NSBitmapImageRep().alloc();
863 int bitmapData = imageRep.bitmapData();
864 if (data.bitmapDataAddress !is 0) OS.free(data.bitmapDataAddress);
865 data.bitmapDataAddress = OS.malloc(4);
866 OS.memmove(data.bitmapDataAddress, new int[] {bitmapData}, 4);
867 rep = rep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_(
868 data.bitmapDataAddress, width, height, 8, 3, false, false, new NSString(OS.NSDeviceRGBColorSpace()), OS.NSAlphaFirstBitmapFormat , bpr, 32);
869 rep.autorelease();
870 }
871 NSGraphicsContext context = NSGraphicsContext.graphicsContextWithBitmapImageRep(rep);
872 NSGraphicsContext.setCurrentContext(context);
873 NSAffineTransform transform = NSAffineTransform.transform();
874 NSSize size = handle.size();
875 transform.translateXBy(0, size.height);
876 transform.scaleXBy(1, -1);
877 transform.set();
878 if (data !is null) {
879 int mask = DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
880 if ((data.style & mask) is 0) {
881 data.style |= DWT.LEFT_TO_RIGHT;
882 }
883 data.device = device;
884 data.background = device.COLOR_WHITE.handle;
885 data.foreground = device.COLOR_BLACK.handle;
886 data.font = device.systemFont;
887 data.image = this;
888 }
889 NSGraphicsContext.setCurrentContext(current);
890 return context.id;
891 }
892
893 /**
894 * Invokes platform specific functionality to dispose a GC handle.
895 * <p>
896 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
897 * API for <code>Image</code>. It is marked public only so that it
898 * can be shared within the packages provided by DWT. It is not
899 * available on all platforms, and should never be called from
900 * application code.
901 * </p>
902 *
903 * @param hDC the platform specific GC handle
904 * @param data the platform specific GC data
905 */
906 public void internal_dispose_GC (int context, GCData data) {
907 if (data.bitmapDataAddress !is 0) OS.free(data.bitmapDataAddress);
908 data.bitmapDataAddress = 0;
909 }
910
911 /**
912 * Returns <code>true</code> if the image has been disposed,
913 * and <code>false</code> otherwise.
914 * <p>
915 * This method gets the dispose state for the image.
916 * When an image has been disposed, it is an error to
917 * invoke any other method using the image.
918 *
919 * @return <code>true</code> when the image is disposed and <code>false</code> otherwise
920 */
921 public bool isDisposed() {
922 return handle is null;
923 }
924
925 /**
926 * Sets the color to which to map the transparent pixel.
927 * <p>
928 * There are certain uses of <code>Images</code> that do not support
929 * transparency (for example, setting an image into a button or label).
930 * In these cases, it may be desired to simulate transparency by using
931 * the background color of the widget to paint the transparent pixels
932 * of the image. This method specifies the color that will be used in
933 * these cases. For example:
934 * <pre>
935 * Button b = new Button();
936 * image.setBackground(b.getBackground());
937 * b.setImage(image);
938 * </pre>
939 * </p><p>
940 * The image may be modified by this operation (in effect, the
941 * transparent regions may be filled with the supplied color). Hence
942 * this operation is not reversible and it is not legal to call
943 * this function twice or with a null argument.
944 * </p><p>
945 * This method has no effect if the receiver does not have a transparent
946 * pixel value.
947 * </p>
948 *
949 * @param color the color to use when a transparent pixel is specified
950 *
951 * @exception IllegalArgumentException <ul>
952 * <li>ERROR_NULL_ARGUMENT - if the color is null</li>
953 * <li>ERROR_INVALID_ARGUMENT - if the color has been disposed</li>
954 * </ul>
955 * @exception DWTException <ul>
956 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
957 * </ul>
958 */
959 public void setBackground(Color color) {
960 if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED);
961 if (color is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
962 if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
963 if (transparentPixel is -1) return;
964 // byte red = (byte)((transparentPixel >> 16) & 0xFF);
965 // byte green = (byte)((transparentPixel >> 8) & 0xFF);
966 // byte blue = (byte)((transparentPixel >> 0) & 0xFF);
967 // byte newRed = (byte)((int)(color.handle[0] * 255) & 0xFF);
968 // byte newGreen = (byte)((int)(color.handle[1] * 255) & 0xFF);
969 // byte newBlue = (byte)((int)(color.handle[2] * 255) & 0xFF);
970 // int height = OS.CGImageGetHeight(handle);
971 // int bpl = OS.CGImageGetBytesPerRow(handle);
972 // byte[] line = new byte[bpl];
973 // for (int i = 0, offset = 0; i < height; i++, offset += bpl) {
974 // OS.memmove(line, data + offset, bpl);
975 // for (int j = 0; j < line.length; j += 4) {
976 // if (line[j+ 1] is red && line[j + 2] is green && line[j + 3] is blue) {
977 // line[j + 1] = newRed;
978 // line[j + 2] = newGreen;
979 // line[j + 3] = newBlue;
980 // }
981 // }
982 // OS.memmove(data + offset, line, bpl);
983 // }
984 // transparentPixel = (newRed & 0xFF) << 16 | (newGreen & 0xFF) << 8 | (newBlue & 0xFF);
985 }
986
987 /**
988 * Returns a string containing a concise, human-readable
989 * description of the receiver.
990 *
991 * @return a string representation of the receiver
992 */
993 public String toString () {
994 if (isDisposed()) return "Image {*DISPOSED*}";
995 return "Image {" + handle + "}";
996 }
997
998 }