source: trunk/src/org/expeditee/gio/javafx/JavaFXImageManager.java@ 1097

Last change on this file since 1097 was 1097, checked in by davidb, 6 years ago

Newly structured files from Corey's work on logic/graphics separation

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