source: trunk/src/org/expeditee/gio/swing/SwingImageManager.java@ 1427

Last change on this file since 1427 was 1427, checked in by bln4, 5 years ago

New piping and functionality implementation to support the encrypting of images.

File size: 15.1 KB
Line 
1package org.expeditee.gio.swing;
2
3import java.awt.Graphics;
4import java.awt.Graphics2D;
5import java.awt.GraphicsEnvironment;
6import java.awt.Rectangle;
7import java.awt.Toolkit;
8import java.awt.image.BufferedImage;
9import java.awt.image.CropImageFilter;
10import java.awt.image.FilteredImageSource;
11import java.awt.image.ImageObserver;
12import java.awt.image.MemoryImageSource;
13import java.awt.image.PixelGrabber;
14import java.awt.image.VolatileImage;
15import java.io.File;
16import java.io.IOException;
17import java.net.HttpURLConnection;
18import java.net.URL;
19import java.util.HashMap;
20
21import javax.imageio.ImageIO;
22
23import org.expeditee.core.Colour;
24import org.expeditee.core.Image;
25import org.expeditee.core.bounds.AxisAlignedBoxBounds;
26import org.expeditee.gio.ImageManager;
27import org.expeditee.gui.DisplayController;
28
29import com.sun.pdfview.PDFPage;
30
31/**
32 * The Swing version of the image manager. Mappings are maintained through
33 * the Image handle number so that handles can be garbage-collected i.e. this
34 * manager doesn't hold any reference to the handle object itself. Image.finalize()
35 * tells this manager when the handle is being collected so the mapping can be
36 * removed.
37 *
38 * @author cts16
39 */
40public class SwingImageManager extends ImageManager {
41
42 /** Singleton instance. */
43 private static SwingImageManager _instance;
44
45 /** Singleton instantiator. */
46 public static SwingImageManager getInstance()
47 {
48 if (_instance == null) _instance = new SwingImageManager();
49
50 return _instance;
51 }
52
53 /** Mapping from image handles to actual internal images. */
54 private HashMap<Long, java.awt.Image> _imageMap;
55
56 /** Mapping from image handles to image animators (for animated images e.g. GIFs). */
57 private HashMap<Long, SelfAnimatingImageObserver> _animatorMap;
58
59 /** Constructor. */
60 private SwingImageManager()
61 {
62 // Initialise the maps
63 _imageMap = new HashMap<Long, java.awt.Image>();
64 _animatorMap = new HashMap<Long, SelfAnimatingImageObserver>();
65 }
66
67 @Override
68 public org.expeditee.core.Image createImage(int width, int height, boolean hintUseAcceleratedMemory)
69 {
70 // Ensure width and height are sane
71 if (width <= 0 || height <= 0) return null;
72
73 java.awt.Image swingImage;
74
75 if (hintUseAcceleratedMemory) {
76 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
77 swingImage = ge.getDefaultScreenDevice().getDefaultConfiguration().createCompatibleVolatileImage(width, height);
78 } else {
79 swingImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
80 }
81
82 return register(swingImage, false);
83 }
84
85 @Override
86 public org.expeditee.core.Image createImage(int width, int height, int[] pixelData)
87 {
88 // Ensure width and height are sane
89 if (width <= 0 || height <= 0) return null;
90
91 // Make sure the caller has provided enough pixel data
92 if (pixelData == null || pixelData.length < (width * height)) return null;
93
94 // Create the Swing image
95 java.awt.Image swingImage;
96 MemoryImageSource memory_image = new MemoryImageSource(width, height, pixelData, 0, width);
97 swingImage = Toolkit.getDefaultToolkit().createImage(memory_image);
98
99 // Register the image and return its handle
100 return register(swingImage, false);
101 }
102
103 @Override
104 public org.expeditee.core.Image createImage(int width, int height, PDFPage page)
105 {
106 // Ensure width and height are sane
107 if (width <= 0 || height <= 0) return null;
108
109 // Ensure we have a valid PDFPage
110 if (page == null) return null;
111
112 // Get the Swing image of the page
113 java.awt.Image swingImage = page.getImage(width, height, new Rectangle(0, 0, (int)page.getBBox().getWidth(), (int)page.getBBox().getHeight()), null, true, true);
114
115 // Register it and return the handle
116 return register(swingImage, false);
117 }
118
119 @Override
120 public org.expeditee.core.Image createImage(byte[] rawImageData) {
121 // Get the image from the given file
122 java.awt.Image swingImage = Toolkit.getDefaultToolkit().createImage(rawImageData);
123 if (swingImage == null) return null;
124
125 // Register and return the handle
126 return register(swingImage, true);
127 }
128
129 /**
130 * Registers the given swing image with the Expeditee image system. Not available from the abstract interface.
131 *
132 * @param image
133 * The image to register.
134 *
135 * @return The internal image handle.
136 */
137 public org.expeditee.core.Image createImage(java.awt.Image image)
138 {
139 return register(image, false);
140 }
141
142 @Override
143 public org.expeditee.core.Image createImageAsCroppedCopy(org.expeditee.core.Image orig, int x, int y, int width, int height)
144 {
145 // Ensure width and height are sane
146 if (width <= 0 || height <= 0) return null;
147
148 // Make sure the original is a valid image
149 java.awt.Image swingOrig = getInternalImage(orig);
150 if (swingOrig == null) return null;
151
152 // Make sure the given coordinate are within the bounds of the image
153 AxisAlignedBoxBounds selectedArea = new AxisAlignedBoxBounds(x, y, width, height);
154 if (!orig.getBounds().completelyContains(selectedArea)) return null;
155
156 // Create a new image that is a cropped version of the original
157 java.awt.Image swingImage = Toolkit.getDefaultToolkit().createImage(
158 new FilteredImageSource(swingOrig.getSource(),
159 new CropImageFilter(x, y, width, height)
160 )
161 );
162
163 // Register and return the handle
164 return register(swingImage, false);
165 }
166
167 @Override
168 public org.expeditee.core.Image getImage(URL url)
169 {
170 // Make sure url is valid
171 if (url == null) return null;
172
173 // Get the image from the given URL
174 java.awt.Image swingImage = Toolkit.getDefaultToolkit().createImage(url);
175 if (swingImage == null) return null;
176
177 // Register and return the handle
178 return register(swingImage, true);
179 }
180
181 @Override
182 public org.expeditee.core.Image getImage(String filename)
183 {
184 // Make sure the filename is valid
185 if (filename == null) return null;
186
187 // Get the image from the given file
188 java.awt.Image swingImage = Toolkit.getDefaultToolkit().createImage(filename);
189 if (swingImage == null) return null;
190
191 // Register and return the handle
192 return register(swingImage, true);
193 }
194
195 @Override
196 public org.expeditee.core.Image getImage(HttpURLConnection connection) throws IOException
197 {
198 // Make sure the connection is valid
199 if (connection == null) return null;
200
201 // Spoofing a widely accepted User Agent, since some sites refuse to serve non-webbrowser clients
202 connection.setRequestProperty("User-Agent", "Mozilla/5.0");
203
204 // Read the image from the connection
205 BufferedImage swingImage = ImageIO.read(connection.getInputStream());
206 if (swingImage == null) return null;
207
208 // Register and return the handle
209 return register(swingImage, false);
210 }
211
212 @Override
213 public synchronized void releaseImage(org.expeditee.core.Image image)
214 {
215 // Make sure the handle corresponds to a valid Swing image
216 java.awt.Image swingImage = getInternalImage(image);
217 if (swingImage == null) return;
218
219 // Release and forget about the Swing image
220 swingImage.flush();
221 _imageMap.remove(image.getHandle());
222
223 // If this image is animated, stop the animator
224 SelfAnimatingImageObserver obs = getAnimator(image);
225 if (obs != null) {
226 obs.deactivate();
227 _animatorMap.remove(image.getHandle());
228 }
229 }
230
231 @Override
232 public synchronized boolean isImageValid(Image image)
233 {
234 // Null image is invalid
235 if (image == null) return false;
236
237 // Image is valid if it's in the map
238 return _imageMap.containsKey(image.getHandle());
239 }
240
241 @Override
242 public int getWidth(org.expeditee.core.Image image)
243 {
244 // Make sure the image is valid
245 java.awt.Image swingImage = getInternalImage(image);
246 if (swingImage == null) return Image.INVALID_SIZE;
247
248 // Create a blocking observer so we don't return without an answer
249 BlockingImageObserver obs = new BlockingImageObserver(ImageObserver.WIDTH);
250
251 // Try and get the width now if it's available
252 int width = swingImage.getWidth(obs);
253
254 // If it's not available yet, block until it is
255 if (width == Image.INVALID_SIZE) {
256 obs.attemptWait();
257 width = obs.width;
258 }
259
260 return width;
261 }
262
263 @Override
264 public int getHeight(org.expeditee.core.Image image)
265 {
266 // Make sure the image is valid
267 java.awt.Image swingImage = getInternalImage(image);
268 if (swingImage == null) return Image.INVALID_SIZE;
269
270 // Create a blocking observer so we don't return without an answer
271 BlockingImageObserver obs = new BlockingImageObserver(ImageObserver.HEIGHT);
272
273 // Try and get the height now if it's available
274 int height = swingImage.getHeight(obs);
275
276 // If it's not available yet, block until it is
277 if (height == Image.INVALID_SIZE) {
278 obs.attemptWait();
279 height = obs.height;
280 }
281
282 return height;
283 }
284
285 @Override
286 public Colour[] getPixels(org.expeditee.core.Image image, int x, int y, int width, int height)
287 {
288 // Make sure we have a valid image
289 java.awt.Image swingImage = getInternalImage(image);
290 if (swingImage == null) return null;
291
292 // Make sure width and height are reasonable
293 if (width <= 0 || height <= 0) return null;
294
295 // Make sure the given coordinate are within the bounds of the image
296 AxisAlignedBoxBounds selectedArea = new AxisAlignedBoxBounds(x, y, width, height);
297 if (!image.getBounds().completelyContains(selectedArea)) return null;
298
299 // Create an pixel grabber to get the pixel values
300 int[] pixels = new int[width * height];
301 PixelGrabber grabber = new PixelGrabber(swingImage, x, y, width, height, pixels, 0, width);
302
303 // Block until we've grabbed the pixels
304 boolean pixelsGrabbed = false;
305 while(!pixelsGrabbed) {
306 try {
307 grabber.grabPixels(0);
308 pixelsGrabbed = true;
309 } catch (InterruptedException e) {
310 }
311 }
312
313 // Return the colour of the grabbed pixel
314 Colour[] ret = new Colour[width * height];
315 for (int i = 0; i < (width * height); i++) {
316 ret[i] = Colour.fromARGB32BitPacked(pixels[i]);
317 }
318 return ret;
319 }
320
321 @Override
322 public void setPixel(org.expeditee.core.Image image, int x, int y, Colour c)
323 {
324 // Make sure we have a valid image
325 java.awt.Image swingImage = getInternalImage(image);
326 if (swingImage == null) return;
327
328 // Make sure the given coordinate is inside the image
329 if (!image.getBounds().contains(x, y)) return;
330
331 // Draw to the pixel
332 Graphics g = swingImage.getGraphics();
333 g.setColor(SwingConversions.toSwingColor(c));
334 g.fillRect(x, y, 1, 1);
335 }
336
337 @Override
338 public boolean writeImageToDisk(org.expeditee.core.Image image, String format, File file) throws IOException
339 {
340 // Validate parameters
341 if (format == null || file == null) return false;
342
343 // Make sure we have a valid image
344 java.awt.Image swingImage = getInternalImage(image);
345 if (swingImage == null) return false;
346
347 // Write the image to disk
348 if (swingImage instanceof BufferedImage) {
349 return ImageIO.write((BufferedImage) swingImage, format, file);
350 } else {
351 assert(swingImage instanceof VolatileImage);
352 return ImageIO.write(((VolatileImage) swingImage).getSnapshot(), format, file);
353 }
354 }
355
356 /** Gets the graphics context for drawing on this image. */
357 public Graphics2D getImageGraphics(Image image)
358 {
359 // Make sure we have a valid image
360 java.awt.Image swingImage = getInternalImage(image);
361 if (swingImage == null) return null;
362
363 // Get the graphics context for the image
364 try {
365 if (swingImage instanceof VolatileImage) {
366 return ((VolatileImage) swingImage).createGraphics();
367 } else {
368 assert (swingImage instanceof BufferedImage);
369 return ((BufferedImage) swingImage).createGraphics();
370 }
371 } catch (Exception e) {
372 e.printStackTrace(System.out);
373 return null;
374 }
375 }
376
377 /** Gets the Swing image associated with the given handle, or null if none is. */
378 public synchronized java.awt.Image getInternalImage(org.expeditee.core.Image image)
379 {
380 // Make sure we have a valid image
381 if (!isImageValid(image)) return null;
382
383 // Return the internal image
384 return _imageMap.get(image.getHandle());
385 }
386
387 /** Creates and returns a handle for the given Swing image to use for future reference. */
388 private synchronized org.expeditee.core.Image register(java.awt.Image swingImage, boolean fromDisk)
389 {
390 // TODO: Add animators. cts16
391 // TODO: Only add animators for images we know are animated. cts16
392
393 // Create a new handle for the image
394 org.expeditee.core.Image image = Image.get(fromDisk);
395
396 // Put the handle and image in the map
397 _imageMap.put(image.getHandle(), swingImage);
398
399 // Return the handle
400 return image;
401 }
402
403 /**
404 * Gets an animator for the given image. Will reuse an old one if it exists,
405 * or create one if not.
406 */
407 public synchronized SelfAnimatingImageObserver getAnimator(org.expeditee.core.Image image)
408 {
409 SelfAnimatingImageObserver obs = _animatorMap.get(image.getHandle());
410 if (obs == null) {
411 obs = new SelfAnimatingImageObserver();
412 _animatorMap.put(image.getHandle(), obs);
413 }
414 return _animatorMap.get(image.getHandle());
415 }
416
417 /** TODO: Comment. cts16 */
418 public java.awt.image.BufferedImage getBufferedImageForImage(org.expeditee.core.Image image)
419 {
420 if (image == null) return null;
421
422 java.awt.Image swingImage = getInternalImage(image);
423 if (swingImage instanceof VolatileImage) {
424 return ((VolatileImage) swingImage).getSnapshot();
425 } else if (swingImage instanceof sun.awt.image.ToolkitImage) {
426 return ((sun.awt.image.ToolkitImage) swingImage).getBufferedImage();
427 } else {
428 assert(swingImage instanceof BufferedImage);
429 return (BufferedImage) swingImage;
430 }
431 }
432
433 /**
434 * Image observer which blocks the calling thread until image is fully loaded.
435 */
436 public static class BlockingImageObserver implements ImageObserver
437 {
438 private int _waitingOn;
439 private boolean _done = false;
440
441 public int x, y, width, height;
442
443 public BlockingImageObserver()
444 {
445 this(ImageObserver.ALLBITS);
446 }
447
448 public BlockingImageObserver(int waitingOn)
449 {
450 _waitingOn = waitingOn;
451 }
452
453 @Override
454 public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height) {
455
456 if ((infoflags & _waitingOn) == _waitingOn) {
457 this.x = x;
458 this.y = y;
459 this.width = width;
460 this.height = height;
461 synchronized (this) {
462 _done = true;
463 notify();
464 }
465 return true;
466 }
467
468 return false;
469 }
470
471 public void attemptWait()
472 {
473 synchronized (this) {
474 while (!_done) {
475 try {
476 wait();
477 } catch (InterruptedException e) {
478
479 }
480 }
481 }
482 }
483
484 }
485
486 /**
487 * Image observer which refreshes the display to show a new frame from an animated image.
488 */
489 public static class SelfAnimatingImageObserver implements ImageObserver
490 {
491 private boolean _active;
492
493 public SelfAnimatingImageObserver()
494 {
495 _active = true;
496 }
497
498 public void activate()
499 {
500 _active = true;
501 }
502
503 public void deactivate()
504 {
505 _active = false;
506 }
507
508 @Override
509 public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height)
510 {
511 if (!_active) return false;
512
513 DisplayController.requestRefresh(false);
514
515 return true;
516 }
517
518 }
519
520 /**
521 * Image observer which refreshes the display once an image finishes loading.
522 */
523 public static class LoadCompletedImageObserver implements ImageObserver
524 {
525 @Override
526 public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height)
527 {
528 if ((infoflags & ImageObserver.ALLBITS) != 0) {
529 DisplayController.requestRefresh(false);
530 return false;
531 }
532
533 return true;
534 }
535 }
536
537}
Note: See TracBrowser for help on using the repository browser.