source: trunk/src/org/expeditee/gio/javafx/JavaFXImageManager.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: 9.6 KB
Line 
1package org.expeditee.gio.javafx;
2
3import java.io.File;
4import java.io.IOException;
5import java.net.HttpURLConnection;
6import java.net.MalformedURLException;
7import java.net.URL;
8import java.util.HashMap;
9
10import javax.imageio.ImageIO;
11
12import org.expeditee.core.Colour;
13import org.expeditee.core.Image;
14import org.expeditee.core.bounds.AxisAlignedBoxBounds;
15import org.expeditee.gio.ImageManager;
16
17import com.sun.pdfview.PDFPage;
18
19import javafx.embed.swing.SwingFXUtils;
20import javafx.scene.image.PixelFormat;
21import javafx.scene.image.WritableImage;
22import sun.reflect.generics.reflectiveObjects.NotImplementedException;
23
24public class JavaFXImageManager extends ImageManager {
25
26 /** Singleton instance. */
27 private static JavaFXImageManager _instance;
28
29 /** Singleton instantiator. */
30 public static JavaFXImageManager getInstance()
31 {
32 if (_instance == null) _instance = new JavaFXImageManager();
33
34 return _instance;
35 }
36
37 /** Mapping from image handles to actual internal images. */
38 private HashMap<Long, javafx.scene.image.WritableImage> _imageMap;
39
40 /** Constructor. */
41 private JavaFXImageManager()
42 {
43 // Initialise the map
44 _imageMap = new HashMap<Long, javafx.scene.image.WritableImage>();
45 }
46
47 @Override
48 public Image createImage(int width, int height, boolean hintUseAcceleratedMemory)
49 {
50 // Note: hintUseAcceleratedMemory is ignored. cts16
51
52 // Ensure width and height are sane
53 if (width <= 0 || height <= 0) return null;
54
55 // Create the image
56 WritableImage jfxImage = new WritableImage(width, height);
57
58 // Register and return the handle
59 return register(jfxImage, false);
60 }
61
62 @Override
63 public Image createImage(int width, int height, int[] pixelData)
64 {
65 // Ensure width and height are sane
66 if (width <= 0 || height <= 0) return null;
67
68 // Make sure the caller has provided enough pixel data
69 if (pixelData == null || pixelData.length < (width * height)) return null;
70
71 // Create the image
72 WritableImage jfxImage = new WritableImage(width, height);
73
74 // Copy the pixel data to the image
75 jfxImage.getPixelWriter().setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), pixelData, 0, width);
76
77 // Register and return the handle
78 return register(jfxImage, false);
79 }
80
81 @Override
82 public Image createImage(int width, int height, PDFPage page)
83 {
84 // TODO: Currently isn't implemented properly. cts16
85 return createImage(width, height);
86 }
87
88 /** Create a registration for an internal-type image. Not part of the general public interface. */
89 public Image createImage(javafx.scene.image.Image image)
90 {
91 // Make sure we have a valid image
92 if (image == null || image.getWidth() == 0.0 || image.getHeight() == 0.0) return null;
93
94 // Create a writable copy of the original
95 javafx.scene.image.WritableImage writableImage = new WritableImage(image.getPixelReader(), (int) image.getWidth(), (int) image.getHeight());
96
97 // Register the image
98 return register(writableImage, false);
99 }
100
101
102 @Override
103 public Image createImage(byte[] rawImageData) {
104 throw new NotImplementedException();
105 }
106
107 @Override
108 public Image createImageAsCroppedCopy(Image orig, int x, int y, int width, int height)
109 {
110 // Ensure width and height are sane
111 if (width <= 0 || height <= 0) return null;
112
113 // Make sure the original is a valid image
114 javafx.scene.image.WritableImage jfxOrig = getInternalImage(orig);
115 if (jfxOrig == null) return null;
116
117 // Make sure the given coordinate are within the bounds of the image
118 AxisAlignedBoxBounds selectedArea = new AxisAlignedBoxBounds(x, y, width, height);
119 if (!orig.getBounds().completelyContains(selectedArea)) return null;
120
121 // Create a new image that is the cropped size
122 javafx.scene.image.WritableImage jfxCropped = new WritableImage(jfxOrig.getPixelReader(), x, y, width, height);
123
124 // Register and return the handle
125 return register(jfxCropped, false);
126 }
127
128 @Override
129 public Image getImage(URL url)
130 {
131 // JavaFX loads all images via a string url
132 return getImage(url.toString());
133 }
134
135 @Override
136 public Image getImage(String filename)
137 {
138 // Make sure we have a filename
139 if (filename == null) return null;
140
141 // Make sure we include the file protocol for files
142 if (!filename.startsWith("file")) {
143 try {
144 URL url = new URL("file", null, filename);
145 filename = url.toString();
146 } catch (MalformedURLException e) {
147 e.printStackTrace();
148 return null;
149 }
150 }
151 // Load the file as read only
152 javafx.scene.image.Image jfxReadOnlyImage;
153 try {
154 jfxReadOnlyImage = new javafx.scene.image.Image(filename, false);
155 } catch (Exception e) {
156 return null;
157 }
158
159 // Create the writable copy
160 javafx.scene.image.WritableImage jfxImage = new javafx.scene.image.WritableImage(jfxReadOnlyImage.getPixelReader(), (int) jfxReadOnlyImage.getWidth(), (int) jfxReadOnlyImage.getHeight());
161
162 // Register and return the handle
163 return register(jfxImage, false);
164 }
165
166 @Override
167 public Image getImage(HttpURLConnection connection) throws IOException
168 {
169 // Make sure the connection is valid
170 if (connection == null) return null;
171
172 // Load the image as read only
173 javafx.scene.image.Image jfxReadOnlyImage = new javafx.scene.image.Image(connection.getInputStream());
174
175 // Block until jfxReadOnlyImage has a valid width/height
176 ensureLoaded(jfxReadOnlyImage);
177
178 // Create the writable copy
179 javafx.scene.image.WritableImage jfxImage = new javafx.scene.image.WritableImage(jfxReadOnlyImage.getPixelReader(), (int) jfxReadOnlyImage.getWidth(), (int) jfxReadOnlyImage.getHeight());
180
181 // Register and return the handle
182 return register(jfxImage, false);
183 }
184
185 @Override
186 public synchronized void releaseImage(org.expeditee.core.Image image)
187 {
188 // Make sure the handle corresponds to a valid JavaFX image
189 javafx.scene.image.WritableImage jfxImage = getInternalImage(image);
190 if (jfxImage == null) return;
191
192 // Release and forget about the JavaFX image
193 if (jfxImage != null) {
194 _imageMap.remove(image.getHandle());
195 }
196 }
197
198 @Override
199 public synchronized boolean isImageValid(Image image)
200 {
201 // Null image is invalid
202 if (image == null) return false;
203
204 // Image is valid if it's in the map
205 return _imageMap.containsKey(image.getHandle());
206 }
207
208 @Override
209 public int getWidth(Image image)
210 {
211 // Make sure the handle corresponds to a valid JavaFX image
212 javafx.scene.image.WritableImage jfxImage = getInternalImage(image);
213 if (jfxImage == null) return Image.INVALID_SIZE;
214
215 // Return the width
216 return (int) jfxImage.getWidth();
217 }
218
219 @Override
220 public int getHeight(Image image)
221 {
222 // Make sure the handle corresponds to a valid JavaFX image
223 javafx.scene.image.WritableImage jfxImage = getInternalImage(image);
224 if (jfxImage == null) return Image.INVALID_SIZE;
225
226 // Return the height
227 return (int) jfxImage.getHeight();
228 }
229
230 @Override
231 public Colour[] getPixels(Image image, int x, int y, int width, int height)
232 {
233 // Make sure we have a valid image
234 javafx.scene.image.WritableImage jfxImage = getInternalImage(image);
235 if (jfxImage == null) return null;
236
237 // Make sure width and height are reasonable
238 if (width <= 0 || height <= 0) return null;
239
240 // Make sure the given coordinate are within the bounds of the image
241 AxisAlignedBoxBounds selectedArea = new AxisAlignedBoxBounds(x, y, width, height);
242 if (!image.getBounds().completelyContains(selectedArea)) return null;
243
244 // Create an array to get the pixel values
245 int[] pixels = new int[width * height];
246
247 // Read the values of the desired pixels
248 jfxImage.getPixelReader().getPixels(x, y, width, height, PixelFormat.getIntArgbInstance(), pixels, 0, width);
249
250 // Return the colour of the grabbed pixel
251 Colour[] ret = new Colour[width * height];
252 for (int i = 0; i < (width * height); i++) {
253 ret[i] = Colour.fromARGB32BitPacked(pixels[i]);
254 }
255 return ret;
256 }
257
258 @Override
259 public void setPixel(Image image, int x, int y, Colour c)
260 {
261 // Make sure we have a valid image
262 javafx.scene.image.WritableImage jfxImage = getInternalImage(image);
263 if (jfxImage == null) return;
264
265 // Make sure we are inside the image
266 if (!image.getBounds().contains(x, y)) return;
267
268 // Set the colour of the pixel
269 jfxImage.getPixelWriter().setColor(x, y, JavaFXConversions.toJavaFXColor(c));
270 }
271
272 @Override
273 public boolean writeImageToDisk(Image image, String format, File file) throws IOException
274 {
275 // Validate parameters
276 if (format == null || file == null) return false;
277
278 // Make sure we have a valid image
279 javafx.scene.image.Image jfxImage = getInternalImage(image);
280 if (jfxImage == null) return false;
281
282 // Create a Swing image (the internets said this is how you do it :P)
283 // TODO: see if there is a Swing-less way of doing this. cts16
284 java.awt.image.BufferedImage swingImage = SwingFXUtils.fromFXImage(jfxImage, null);
285
286 // Write the image to disk
287 return ImageIO.write(swingImage, format, file);
288 }
289
290 /** Creates and returns a handle for the given JavaFX image to use for future reference. */
291 private synchronized Image register(javafx.scene.image.WritableImage jfxImage, boolean fromDisk)
292 {
293 // TODO: Add animators. cts16
294 // TODO: Only add animators for images we know are animated. cts16
295
296 Image image = Image.get(fromDisk);
297
298 _imageMap.put(image.getHandle(), jfxImage);
299
300 return image;
301 }
302
303 /** Gets the JavaFX image associated with the given handle, or null if none is. */
304 public synchronized javafx.scene.image.WritableImage getInternalImage(Image image)
305 {
306 if (!isImageValid(image)) return null;
307
308 return _imageMap.get(image.getHandle());
309 }
310
311 /** Blocks the calling thread until the given image is fully loaded. */
312 private void ensureLoaded(javafx.scene.image.Image jfxImage)
313 {
314 JavaFXMiscManager.waitUntilPropertyEquals(jfxImage.progressProperty(), 1.0);
315 }
316}
Note: See TracBrowser for help on using the repository browser.