|
1 /******************************************************************************* |
|
2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). |
|
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 * Nokia Corporation - initial implementation |
|
10 *******************************************************************************/ |
|
11 package org.eclipse.swt.internal.qt.graphics; |
|
12 |
|
13 |
|
14 import org.eclipse.swt.graphics.ImageData; |
|
15 import org.eclipse.swt.widgets.Display; |
|
16 import org.eclipse.swt.widgets.Internal_PackageSupport; |
|
17 |
|
18 public final class Image { |
|
19 |
|
20 /** |
|
21 * Specifies no transformation. |
|
22 */ |
|
23 public static final int TRANS_NONE = 100; |
|
24 /** |
|
25 * Specifies transformation for 90 degrees clockwise |
|
26 * along z-axis. |
|
27 */ |
|
28 public static final int TRANS_ROT90 = 101; |
|
29 /** |
|
30 * Specifies transformation for 180 degrees clockwise |
|
31 * along z-axis. |
|
32 */ |
|
33 public static final int TRANS_ROT180 = 102; |
|
34 /** |
|
35 * Specifies transformation for 270 degrees clockwise |
|
36 * along z-axis. |
|
37 */ |
|
38 public static final int TRANS_ROT270 = 103; |
|
39 /** |
|
40 * Specifies transformation for image mirror along y-axis. |
|
41 */ |
|
42 public static final int TRANS_MIRROR = 104; |
|
43 /** |
|
44 * Specifies transformation for first image mirror along y-axis |
|
45 * and then 90 degrees rotation clockwise along z-axis. |
|
46 */ |
|
47 public static final int TRANS_MIRROR_ROT90 = 105; |
|
48 /** |
|
49 * Specifies transformation for first image mirror along y-axis |
|
50 * and then 180 degrees rotation clockwise along z-axis. |
|
51 */ |
|
52 public static final int TRANS_MIRROR_ROT180 = 106; |
|
53 /** |
|
54 * Specifies transformation for first image mirror along y-axis |
|
55 * and then 270 degrees rotation clockwise along z-axis. |
|
56 */ |
|
57 public static final int TRANS_MIRROR_ROT270 = 107; |
|
58 |
|
59 /** |
|
60 * Specifies transformation for mirror the image along x-axis. |
|
61 */ |
|
62 public static final int TRANS_FLIP_HORIZONTAL = 108; |
|
63 |
|
64 /** |
|
65 * Specifies transformation for mirror the image along y-axis. |
|
66 */ |
|
67 public static final int TRANS_FLIP_VERTICAL = 109; |
|
68 |
|
69 /** |
|
70 * Specifies default image format. |
|
71 */ |
|
72 public static final int FORMAT_IMG_NONE = 0; |
|
73 |
|
74 /** |
|
75 * Specifies 32 bit per pixel image format. |
|
76 */ |
|
77 public static final int FORMAT_IMG_RGB32 = 1; |
|
78 |
|
79 /** |
|
80 * Specifies 32 bit per pixel image format with alpha channel. |
|
81 */ |
|
82 public static final int FORMAT_IMG_ARGB32 = 2; |
|
83 |
|
84 /** |
|
85 * Specifies 32 bit per pixel image format with alpha channel. |
|
86 */ |
|
87 public static final int FORMAT_IMG_ARGB32PREMULTIPLIED = 3; |
|
88 |
|
89 /** |
|
90 * Specifies 16 bit per pixel image format. |
|
91 */ |
|
92 public static final int FORMAT_IMG_RGB16 = 4; |
|
93 |
|
94 /** |
|
95 * Specifies 16 bit per pixel image format. |
|
96 */ |
|
97 public static final int FORMAT_IMG_RGB555 = 5; |
|
98 |
|
99 /** |
|
100 * Specifies 16 bit per pixel image format. |
|
101 */ |
|
102 public static final int FORMAT_IMG_RGB444 = 6; |
|
103 |
|
104 /** |
|
105 * Specifies 16 bit per pixel image format. |
|
106 */ |
|
107 public static final int FORMAT_IMG_RGB4444PREMULTIPLIED = 7; |
|
108 |
|
109 /** |
|
110 * Specifies 1 bit per pixel image format. |
|
111 */ |
|
112 public static final int FORMAT_IMG_MONO = 8; |
|
113 |
|
114 // Native image handle |
|
115 int handle; |
|
116 // Handle of native QPixmap wrapped by image |
|
117 int pixmapHandle; |
|
118 // Status of native peer |
|
119 private boolean disposed; |
|
120 // Image dimensions |
|
121 private int w; |
|
122 private int h; |
|
123 |
|
124 // Image pixel format |
|
125 private int pixelFormat = FORMAT_IMG_NONE; |
|
126 |
|
127 // flag to indicate if this image is currently |
|
128 // bound as rendering target by GraphicsContext |
|
129 private boolean isBound = false; |
|
130 // GraphicsContext bound to this image |
|
131 private GraphicsContext boundingGc = null; |
|
132 |
|
133 // Image manages copies of itself for command buffering of drawImage and drawRegion. |
|
134 private Image commandBufferCopy; |
|
135 private int commandBufferCopyRefs; |
|
136 private boolean commandBufferCopyDirty; |
|
137 |
|
138 // A copy-constructed Image remembers what it's a copy of |
|
139 private Image whatAmIACopyOf; |
|
140 |
|
141 private Image() { |
|
142 } |
|
143 |
|
144 /** |
|
145 * Constructs new image with given native image handle. |
|
146 * |
|
147 * @param imgHandle The handle of valid native image |
|
148 * @throws OutOfMemoryError if imgHandlde is invalid |
|
149 */ |
|
150 Image(int imgHandle) { |
|
151 Utils.validateUiThread(); |
|
152 // validate handle |
|
153 if(imgHandle == 0) { |
|
154 throw new OutOfMemoryError(); |
|
155 } |
|
156 // store handle & get dimensions |
|
157 handle = imgHandle; |
|
158 updateSize(); |
|
159 } |
|
160 |
|
161 /** |
|
162 * Constructs new image filled with default fill color with specified width and height. |
|
163 * |
|
164 * @param width The width of new image |
|
165 * @param height The height of new image |
|
166 * @throws IllegalArgumetException if width or height is equal or less than zero |
|
167 * @throws OutOfMemoryError if memory allocation for new image fails |
|
168 */ |
|
169 public Image(int width, int height) { |
|
170 Utils.validateUiThread(); |
|
171 // check width and height |
|
172 if (width <= 0 || height <= 0) { |
|
173 throw new IllegalArgumentException("width or height is equal or less than 0"); |
|
174 } |
|
175 |
|
176 // Construct image in native side and store the handle |
|
177 handle = OS.image_create(width, height, Config.IMAGE_DEFAULT_FILL_COLOR); // may throw outOfMemoryError |
|
178 // set dimensions |
|
179 updateSize(); |
|
180 } |
|
181 |
|
182 /** |
|
183 * Constructs new image with specified width, height and fill color. |
|
184 * The RGB values passed in is interpreted with the least significant eight bits giving the blue |
|
185 * component, the next eight more significant bits giving the green component, and the next eight |
|
186 * bits giving the red component. The high order byte of this value, i.e. alpha, is ignored. |
|
187 * |
|
188 * @param width The width of new image |
|
189 * @param height The height of new image |
|
190 * @param fillColor The initial fill color for the image in format #00RRGGBB. |
|
191 * @throws IllegalArgumetException if width or height is equal or less than zero |
|
192 * @throws OutOfMemoryError if memory allocation for new image fails |
|
193 */ |
|
194 public Image(int width, int height, int fillColor) { |
|
195 Utils.validateUiThread(); |
|
196 // check width and height |
|
197 if (width <= 0 || height <= 0) { |
|
198 throw new IllegalArgumentException("width or height is equal or less than 0"); |
|
199 } |
|
200 |
|
201 // Construct image in native side and store the handle |
|
202 handle = OS.image_create(width, height, fillColor); // may throw outOfMemoryError |
|
203 // set dimensions |
|
204 updateSize(); |
|
205 } |
|
206 |
|
207 /** |
|
208 * Constructs a new instance of this class based on given image. |
|
209 * |
|
210 * @param sourceImage The image used as source |
|
211 * @throws NullPointerException if sourceImage is null |
|
212 * @throws OutOfMemoryError if memory allocation for new image fails |
|
213 */ |
|
214 public Image(Image sourceImage) { |
|
215 this(sourceImage, 0, 0, 0, 0); |
|
216 } |
|
217 |
|
218 /** |
|
219 * Constructs a new instance of this class based on given image. |
|
220 * If the given copy region is empty, the whole image is copied. |
|
221 * |
|
222 * @param sourceImage The image used as source |
|
223 * @param x The top-left x coordinate of the copy region. |
|
224 * @param y The top-left y coordinate of the copy region. |
|
225 * @param width The width of the copy region. |
|
226 * @param height The height of the copy region. |
|
227 * @throws NullPointerException if sourceImage is null |
|
228 */ |
|
229 public Image(Image sourceImage, int x, int y, int width, int height) { |
|
230 // validate sourceImage |
|
231 if (sourceImage == null) { |
|
232 throw new NullPointerException("img is null"); |
|
233 } |
|
234 createCopy(sourceImage, x, y, width, height); |
|
235 } |
|
236 |
|
237 /** |
|
238 * Creates a new image from given ImageData |
|
239 * @param imageData |
|
240 * @throws NullPointerException if imageData is null |
|
241 */ |
|
242 public Image(ImageData imageData) { |
|
243 if (imageData == null) { |
|
244 throw new NullPointerException("imageData is null"); |
|
245 } |
|
246 handle = OS.image_create(imageData); |
|
247 // set dimensions |
|
248 updateSize(); |
|
249 } |
|
250 |
|
251 /** |
|
252 * Constructs a new instance of this class based on the given rgb data. |
|
253 * |
|
254 * @param argbData a RGB data array where one pixel is specified as 0xAARRGGBB. |
|
255 * @param width The width of the image |
|
256 * @param height The height of the image |
|
257 * @param hasAlpha If true the rgb data has also an alpha channel, |
|
258 * otherwise all pixels are fully opaque, e.g. 0xFFRRGGBB. |
|
259 * @throws NullPointerException if the <code>argbData</code> is null |
|
260 * |
|
261 */ |
|
262 public Image(int[] argbData, int width, int height, boolean hasAlpha) { |
|
263 // input validation |
|
264 if(argbData == null) { |
|
265 throw new NullPointerException("argbData is null"); |
|
266 } |
|
267 // Construct an ge in native side and store the handle |
|
268 handle = OS.image_create(argbData, width, height, hasAlpha); |
|
269 // set dimensions |
|
270 updateSize(); |
|
271 } |
|
272 |
|
273 /** |
|
274 * Disposes the instance if this image and frees memory. |
|
275 * After disposing image the only valid metdod is isDisposed(), other |
|
276 * methods will throw illegalStateException. |
|
277 * |
|
278 * If this instance is being disposed before gc has released it, the |
|
279 * releaseTarget() of binding gc is called automatically. |
|
280 */ |
|
281 public void dispose() { |
|
282 Utils.validateUiThread(); |
|
283 if (handle != 0 || !disposed) { |
|
284 // If this instance is being disposed while |
|
285 // gc is still bound, release binding before disposing. |
|
286 if(isBound) { |
|
287 isBound = false; |
|
288 if(boundingGc != null) { |
|
289 boundingGc.releaseTarget(); |
|
290 } |
|
291 boundingGc = null; |
|
292 } |
|
293 |
|
294 // Delete window surface from cache in case |
|
295 // it is created for this instance |
|
296 SurfaceCache.getInstance().deleteSurface(this); |
|
297 |
|
298 whatAmIACopyOf = null; |
|
299 |
|
300 // dispose native peer |
|
301 OS.image_dispose(handle); |
|
302 handle = 0; |
|
303 pixmapHandle = 0; |
|
304 disposed = true; |
|
305 } |
|
306 } |
|
307 |
|
308 /** |
|
309 * Gets the state of the image, is it disposed or not. |
|
310 * |
|
311 * @return true if this instanace has been disposed, otherwise false |
|
312 */ |
|
313 public boolean isDisposed() { |
|
314 return disposed; |
|
315 } |
|
316 |
|
317 /** |
|
318 * Gets the format of the image. |
|
319 * |
|
320 * @return image format |
|
321 */ |
|
322 public int getFormat() { |
|
323 checkState(); |
|
324 if (pixelFormat == FORMAT_IMG_NONE) { |
|
325 pixelFormat = OS.image_getFormat(handle); |
|
326 } |
|
327 return pixelFormat; |
|
328 } |
|
329 /** |
|
330 * Gets the height of the image in pixels. |
|
331 * |
|
332 * @return The height of this image |
|
333 * @throws IllegalStateException if the image has already been disposed |
|
334 */ |
|
335 public int getHeight() { |
|
336 checkState(); |
|
337 return h; |
|
338 } |
|
339 |
|
340 /** |
|
341 * Gets the width of the image in pixels. |
|
342 * |
|
343 * @return The width of this image |
|
344 * @throws IllegalStateException if the image has already been disposed |
|
345 */ |
|
346 public int getWidth() { |
|
347 checkState(); |
|
348 return w; |
|
349 } |
|
350 |
|
351 /** |
|
352 * Copies image data to given integer array from rectangle specified |
|
353 * by x, y, width and height. |
|
354 * |
|
355 * @param argbData The integer array where image data is copied to |
|
356 * @param offset The index into the array where first pixel is stored |
|
357 * @param scanlength The relative offset in the array between pixles in consecutive rows of the region |
|
358 * @param x The x-coordinate of upper left corner of the region |
|
359 * @param y The y-coordinate of upper left corner of the region |
|
360 * @param width The width of the region |
|
361 * @param height The height of the region |
|
362 * |
|
363 * @throws ArrayIndexOutOfBoundsException if the operation tries to access an element in argbData outside is range |
|
364 * @throws IllegalArgumentException if the absolute value of scanlength is less than width |
|
365 * @throws IllegalArgumentException if area to be retrieved exceed the bounds of the image |
|
366 * @throws NullPointerException if argbData is null |
|
367 * @throws OutOfMemoryError if memory allocation failure occurs during operation |
|
368 * @throws IllegalStateException if the image has already been disposed |
|
369 */ |
|
370 public void getRGB(int[] argbData, int offset, int scanlength, |
|
371 int x, int y, int width, int height) { |
|
372 checkState(); |
|
373 |
|
374 // input validation |
|
375 if(argbData == null) { |
|
376 throw new NullPointerException("argbData is null"); |
|
377 } |
|
378 if(((x+width) > getWidth()) || ((y+height) > getHeight()) || (x < 0) || (y < 0)) { |
|
379 throw new IllegalArgumentException("Area specified is out of image bounds"); |
|
380 } |
|
381 if(scanlength < 0) { |
|
382 // End of first row |
|
383 int a = width - 1; |
|
384 int b = 0; |
|
385 int index = offset + (a - x) + (b -y) * scanlength; |
|
386 if(index > argbData.length - 1) { |
|
387 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
388 } |
|
389 // Beginning of last row |
|
390 a = 0; |
|
391 b = height - 1; |
|
392 index = offset + (a - x) + (b -y) * scanlength; |
|
393 if(index < 0) { |
|
394 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
395 } |
|
396 } else { |
|
397 if((argbData.length < (offset + (scanlength * height))) || offset < 0) { |
|
398 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
399 } |
|
400 } |
|
401 if((scanlength > 0 ? scanlength : -scanlength) < width) { |
|
402 throw new IllegalArgumentException("scanlength is less than width"); |
|
403 } |
|
404 |
|
405 |
|
406 OS.image_getRGB(handle, argbData, offset, scanlength, x, y, width, height); |
|
407 } |
|
408 |
|
409 |
|
410 /**TODO: change comments |
|
411 * Copies image data to given integer array from rectangle specified |
|
412 * by x, y, width and height. |
|
413 * |
|
414 * @param argbData The byte array where image data is copied to |
|
415 * @param transparencyMask the byte array where mask values are copied to |
|
416 * @param offset The index into the array where first pixel is stored |
|
417 * @param scanlength The relative offset in the array between pixles in consecutive rows of the region |
|
418 * @param x The x-coordinate of upper left corner of the region |
|
419 * @param y The y-coordinate of upper left corner of the region |
|
420 * @param width The width of the region |
|
421 * @param height The height of the region |
|
422 * @param format The format in which pixels are stored into argbData |
|
423 * @throws ArrayIndexOutOfBoundsException if the operation tries to access an element in argbData outside is range |
|
424 * @throws IllegalArgumentException if the absolute value of scanlength is less than width |
|
425 * @throws IllegalArgumentException if area to be retrieved exceed the bounds of the image |
|
426 * @throws NullPointerException if argbData is null |
|
427 * @throws OutOfMemoryError if memory allocation failure occurs during operation |
|
428 * @throws IllegalStateException if the image has already been disposed |
|
429 */ |
|
430 public void getRGB(byte[] argbData, byte[] transparencyMask, int offset, int scanlength, |
|
431 int x, int y, int width, int height, int format) { |
|
432 checkState(); |
|
433 |
|
434 // input validation |
|
435 if(argbData == null) { |
|
436 throw new NullPointerException("argbData is null"); |
|
437 } |
|
438 if(transparencyMask == null) { |
|
439 throw new IllegalArgumentException("transparencyMask is null"); |
|
440 } |
|
441 if(((x+width) > getWidth()) || ((y+height) > getHeight()) || (x < 0) || (y < 0)) { |
|
442 throw new IllegalArgumentException("Area specified is out of image bounds"); |
|
443 } |
|
444 if(scanlength < 0) { |
|
445 // End of first row |
|
446 int a = width - 1; |
|
447 int b = 0; |
|
448 int index = offset + (a - x) + (b -y) * scanlength; |
|
449 if(index > argbData.length - 1) { |
|
450 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
451 } |
|
452 if(index > transparencyMask.length - 1) { |
|
453 throw new ArrayIndexOutOfBoundsException("Mask does not fit in given array"); |
|
454 } |
|
455 // Beginning of last row |
|
456 a = 0; |
|
457 b = height - 1; |
|
458 index = offset + (a - x) + (b -y) * scanlength; |
|
459 if(index < 0) { |
|
460 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
461 } |
|
462 } else { |
|
463 if((argbData.length < (offset + (scanlength * height))) || offset < 0) { |
|
464 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
465 } |
|
466 if((transparencyMask.length < (offset + (scanlength * height))) || offset < 0) { |
|
467 throw new ArrayIndexOutOfBoundsException("Mask does not fit in given array"); |
|
468 } |
|
469 } |
|
470 if((scanlength > 0 ? scanlength : -scanlength) < width) { |
|
471 throw new IllegalArgumentException("scanlength is less than width"); |
|
472 } |
|
473 |
|
474 |
|
475 OS.image_getRGB(handle, argbData, transparencyMask,offset, scanlength, x, y, width, height, format); |
|
476 } |
|
477 |
|
478 |
|
479 /** |
|
480 * Copies image data to given integer array from rectangle specified |
|
481 * by x, y, width and height. |
|
482 * |
|
483 * @param argbData The short array where image data is copied to |
|
484 * @param offset The index into the array where first pixel is stored |
|
485 * @param scanlength The relative offset in the array between pixles in consecutive rows of the region |
|
486 * @param x The x-coordinate of upper left corner of the region |
|
487 * @param y The y-coordinate of upper left corner of the region |
|
488 * @param width The width of the region |
|
489 * @param height The height of the region |
|
490 * @param format Image format in which pixels are store into argbData //TODO:add explanation when format are addewd to image. |
|
491 * |
|
492 * @throws ArrayIndexOutOfBoundsException if the operation tries to access an element in argbData outside is range |
|
493 * @throws IllegalArgumentException if the absolute value of scanlength is less than width |
|
494 * @throws IllegalArgumentException if area to be retrieved exceed the bounds of the image |
|
495 * @throws NullPointerException if argbData is null |
|
496 * @throws OutOfMemoryError if memory allocation failure occurs during operation |
|
497 * @throws IllegalStateException if the image has already been disposed |
|
498 */ |
|
499 public void getRGB(short[] argbData, int offset, int scanlength, |
|
500 int x, int y, int width, int height, int format) { |
|
501 checkState(); |
|
502 |
|
503 // input validation |
|
504 if(argbData == null) { |
|
505 throw new NullPointerException("argbData is null"); |
|
506 } |
|
507 if(((x+width) > getWidth()) || ((y+height) > getHeight()) || (x < 0) || (y < 0)) { |
|
508 throw new IllegalArgumentException("Area specified is out of image bounds"); |
|
509 } |
|
510 if(scanlength < 0) { |
|
511 // End of first row |
|
512 int a = width - 1; |
|
513 int b = 0; |
|
514 int index = offset + (a - x) + (b -y) * scanlength; |
|
515 if(index > argbData.length - 1) { |
|
516 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
517 } |
|
518 // Beginning of last row |
|
519 a = 0; |
|
520 b = height - 1; |
|
521 index = offset + (a - x) + (b -y) * scanlength; |
|
522 if(index < 0) { |
|
523 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
524 } |
|
525 } else { |
|
526 if((argbData.length < (offset + (scanlength * height))) || offset < 0) { |
|
527 throw new ArrayIndexOutOfBoundsException("Data does not fit in given array"); |
|
528 } |
|
529 } |
|
530 if((scanlength > 0 ? scanlength : -scanlength) < width) { |
|
531 throw new IllegalArgumentException("scanlength is less than width"); |
|
532 } |
|
533 |
|
534 |
|
535 OS.image_getRGB(handle, argbData, offset, scanlength, x, y, width, height, format); |
|
536 } |
|
537 |
|
538 /** |
|
539 * Pixel level collision detection. |
|
540 */ |
|
541 public static boolean detectCollision(Image image1, int transform1, int p1x, int p1y, int r1x1, int r1y1, int r1x2, int r1y2, |
|
542 Image image2, int transform2, int p2x, int p2y, int r2x1, int r2y1, int r2x2, int r2y2) { |
|
543 |
|
544 return OS.image_detectCollision( |
|
545 image1.getNativePixmapHandle(), transform1, p1x, p1y, r1x1, r1y1, r1x2, r1y2, |
|
546 image2.getNativePixmapHandle(), transform2, p2x, p2y, r2x1, r2y1, r2x2, r2y2); |
|
547 } |
|
548 |
|
549 /** |
|
550 * Transforms image with given transform. |
|
551 * |
|
552 * @throws IllegalArgumentException if transform is not valid |
|
553 * @throws IllegalStateException if the image has already been disposed |
|
554 */ |
|
555 public void transform(int transform) { |
|
556 checkState(); |
|
557 |
|
558 // validate transform |
|
559 if (transform != TRANS_NONE && |
|
560 transform != TRANS_ROT90 && |
|
561 transform != TRANS_ROT180 && |
|
562 transform != TRANS_ROT270 && |
|
563 transform != TRANS_MIRROR && |
|
564 transform != TRANS_MIRROR_ROT90 && |
|
565 transform != TRANS_MIRROR_ROT180 && |
|
566 transform != TRANS_MIRROR_ROT270 ) { |
|
567 throw new IllegalArgumentException("Invalid transform"); |
|
568 } |
|
569 |
|
570 // if no transformation is requested, return |
|
571 if(transform == TRANS_NONE) { |
|
572 return; |
|
573 } |
|
574 |
|
575 // perform transform |
|
576 OS.image_transform(handle, transform); |
|
577 |
|
578 // update width and height |
|
579 updateSize(); |
|
580 } |
|
581 |
|
582 /** |
|
583 * Package private method to infrom this image that |
|
584 * it has been bound as rendering target by GraphicsContext. |
|
585 * |
|
586 * @param gc The GraphicsContext that binds this image as target |
|
587 */ |
|
588 void notifyBind(GraphicsContext gc) { |
|
589 boundingGc = gc; |
|
590 isBound = true; |
|
591 } |
|
592 |
|
593 /** |
|
594 * Package private method to infrom this image that |
|
595 * it is no longer as a rendering target of gc. |
|
596 */ |
|
597 void notifyRelease() { |
|
598 boundingGc = null; |
|
599 isBound = false; |
|
600 } |
|
601 |
|
602 /** |
|
603 * Returns native side handle. |
|
604 * @return the native handle. |
|
605 */ |
|
606 public int getHandle() |
|
607 { |
|
608 checkState(); |
|
609 return handle; |
|
610 } |
|
611 |
|
612 /** |
|
613 * Returns native side QPixmap handle. |
|
614 * @return the native QPixmap handle. |
|
615 */ |
|
616 synchronized public int getNativePixmapHandle() |
|
617 { |
|
618 checkState(); |
|
619 if(pixmapHandle == 0) |
|
620 { |
|
621 // In the current implementation this will return |
|
622 // pointer to the already existing QPixmap (which |
|
623 // will be destroyed with the object), so the |
|
624 // handle does not need to be released manually. |
|
625 pixmapHandle = OS.image_getPixmapHandle(handle); |
|
626 } |
|
627 return pixmapHandle; |
|
628 } |
|
629 |
|
630 /** |
|
631 /** |
|
632 * Creates <code>ImageData</code> objects |
|
633 * @return New object |
|
634 */ |
|
635 public ImageData getImageData() { |
|
636 checkState(); |
|
637 return OS.image_getImageData(handle); |
|
638 } |
|
639 |
|
640 /** |
|
641 * Private helper to check the state of the current instance. |
|
642 */ |
|
643 private void checkState() { |
|
644 Utils.validateUiThread(); |
|
645 if(disposed) { |
|
646 throw new IllegalStateException("Image already disposed"); |
|
647 } |
|
648 } |
|
649 |
|
650 /** |
|
651 * private helper for updating iWidth and iHeight from native image. |
|
652 */ |
|
653 private void updateSize() { |
|
654 w = OS.image_getWidth(handle); |
|
655 h = OS.image_getHeight(handle); |
|
656 } |
|
657 |
|
658 /** |
|
659 * Contructs an instance of Image based on the given <code>imageData</code>. |
|
660 * |
|
661 * @param imageData Image data @see org.eclipse.swt.graphics.ImageData |
|
662 * @return Instance of loaded image |
|
663 * @throws IllegalArgumentException if <code>imageData</code> is null. |
|
664 */ |
|
665 public static Image createImage(ImageData imageData) { |
|
666 if (imageData == null) { |
|
667 throw new IllegalArgumentException("imageData is null"); |
|
668 } |
|
669 int imageHandle = OS.image_create(imageData); |
|
670 return new Image(imageHandle); |
|
671 } |
|
672 |
|
673 /** |
|
674 * Constructs new image with given native QPixmap handle. |
|
675 * @param pixmapHandle Handle of native QPixmap. |
|
676 * @return Instance of loaded image. |
|
677 */ |
|
678 public static Image createImageFromPixmap(int pixmapHandle) { |
|
679 // input validation |
|
680 if(pixmapHandle <= 0) { |
|
681 throw new IllegalArgumentException("Invalid pixmap handle"); |
|
682 } |
|
683 // Construct an ge in native side and store the handle |
|
684 int handle = OS.image_create(pixmapHandle); |
|
685 return new Image(handle); |
|
686 } |
|
687 |
|
688 /** |
|
689 * Obtains a shallow copy of this Image to be placed in the command buffer. |
|
690 * The returned copy must be marked as free by calling freeCommandBufferCopy |
|
691 * when it's no longer needed. |
|
692 * |
|
693 * @return The copy |
|
694 */ |
|
695 Image getCommandBufferCopy() { |
|
696 if(commandBufferCopyDirty) { |
|
697 return copyInUIThread(); |
|
698 } |
|
699 if(commandBufferCopy == null) { |
|
700 commandBufferCopyDirty = false; |
|
701 commandBufferCopy = copyInUIThread(); |
|
702 commandBufferCopyRefs = 0; |
|
703 } |
|
704 commandBufferCopyRefs++; |
|
705 return commandBufferCopy; |
|
706 } |
|
707 |
|
708 /** |
|
709 * Marks a copy returned from getCommandBufferCopy as free. |
|
710 * |
|
711 * @param image |
|
712 * The image returned from getShallowCopy. |
|
713 */ |
|
714 void freeCommandBufferCopy() { |
|
715 if(disposed) { |
|
716 throw new RuntimeException("Image referenced by command buffer has been disposed"); |
|
717 } |
|
718 if(whatAmIACopyOf == null) { |
|
719 throw new RuntimeException("Image not a copy"); |
|
720 } |
|
721 whatAmIACopyOf.freeCommandBufferCopyOfMe(this); |
|
722 } |
|
723 |
|
724 /** |
|
725 * Tells to the Image that it has been modified and any copies returned from |
|
726 * getShallowCopy() have thus become deep copies. |
|
727 */ |
|
728 void pixelDataModified() { |
|
729 if(commandBufferCopy != null) { |
|
730 commandBufferCopyDirty = true; |
|
731 } |
|
732 } |
|
733 |
|
734 /* |
|
735 * Copy-construction |
|
736 */ |
|
737 private void createCopy(Image sourceImage, int x, int y, int width, int height) { |
|
738 // Construct image in native side and store the handle |
|
739 handle = OS.image_create(sourceImage.handle, x, y, width, height); // may throw outOfMemoryError |
|
740 // set dimensions |
|
741 updateSize(); |
|
742 whatAmIACopyOf = sourceImage; |
|
743 } |
|
744 |
|
745 /* |
|
746 * Called on the Image when a copy it has returned from getCommandBufferCopy |
|
747 * is being freed by a call to freeCommandBufferCopy. The Image may be |
|
748 * disposed at this point while the copy is not. |
|
749 */ |
|
750 private void freeCommandBufferCopyOfMe(Image copy) { |
|
751 if(copy != commandBufferCopy) { |
|
752 throw new RuntimeException("Copy doesn't exist, freed multiple times?"); |
|
753 } else { |
|
754 commandBufferCopyRefs--; |
|
755 if(commandBufferCopyRefs <= 0) { |
|
756 commandBufferCopy.dispose(); |
|
757 commandBufferCopy = null; |
|
758 } |
|
759 } |
|
760 } |
|
761 |
|
762 /* |
|
763 * Creates a shallow copy of the Image in the UI thread. |
|
764 */ |
|
765 private Image copyInUIThread() { |
|
766 Display d = Internal_PackageSupport.getDisplayInstance(); |
|
767 final Image copy = new Image(); |
|
768 if(d != null) { |
|
769 d.syncExec(new Runnable() { |
|
770 public void run() { |
|
771 copy.createCopy(Image.this, 0, 0, 0, 0); |
|
772 } |
|
773 }); |
|
774 } |
|
775 if(copy.handle == 0) { |
|
776 return null; |
|
777 } |
|
778 return copy; |
|
779 } |
|
780 } |