Mercurial > projects > dwt-mac
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 } |