22
33import static com .ss .rlib .util .array .ArrayFactory .asArray ;
44import static java .awt .Image .SCALE_DEFAULT ;
5- import static java .nio .file .StandardOpenOption .CREATE ;
6- import static java .nio .file .StandardOpenOption .TRUNCATE_EXISTING ;
7- import static java .nio .file .StandardOpenOption .WRITE ;
5+ import static java .nio .file .StandardOpenOption .*;
86import com .jme3 .asset .AssetManager ;
97import com .jme3 .texture .Texture ;
108import com .ss .editor .Editor ;
119import com .ss .editor .FileExtensions ;
1210import com .ss .editor .annotation .FXThread ;
11+ import com .ss .editor .annotation .FromAnyThread ;
1312import com .ss .editor .config .Config ;
1413import com .ss .editor .file .reader .DDSReader ;
1514import com .ss .editor .file .reader .TGAReader ;
1615import com .ss .editor .ui .Icons ;
1716import com .ss .editor .ui .event .FXEventManager ;
17+ import com .ss .editor .ui .event .impl .ChangedCurrentAssetFolderEvent ;
1818import com .ss .editor .ui .event .impl .DeletedFileEvent ;
1919import com .ss .editor .util .EditorUtil ;
2020import com .ss .rlib .logging .Logger ;
2525import com .ss .rlib .util .Utils ;
2626import com .ss .rlib .util .array .Array ;
2727import com .ss .rlib .util .array .ArrayFactory ;
28+ import com .ss .rlib .util .dictionary .DictionaryFactory ;
29+ import com .ss .rlib .util .dictionary .IntegerDictionary ;
30+ import com .ss .rlib .util .dictionary .ObjectDictionary ;
2831import javafx .embed .swing .SwingFXUtils ;
2932import javafx .scene .image .Image ;
3033import jme3tools .converters .ImageToAwt ;
3841import java .io .IOException ;
3942import java .io .OutputStream ;
4043import java .net .URL ;
44+ import java .net .URLClassLoader ;
4145import java .nio .file .Files ;
4246import java .nio .file .Path ;
4347import java .nio .file .attribute .FileTime ;
@@ -76,6 +80,11 @@ public class JavaFXImageManager {
7680 @ NotNull
7781 private static final Array <String > IMAGE_FORMATS = ArrayFactory .newArray (String .class );
7882
83+ /**
84+ * The size of cached images.
85+ */
86+ private static final int CACHED_IMAGES_SIZE = 30 ;
87+
7988 static {
8089 IMAGE_FORMATS .addAll (FX_FORMATS );
8190 IMAGE_FORMATS .addAll (JME_FORMATS );
@@ -104,12 +113,14 @@ public static boolean isImage(@Nullable final Path file) {
104113 *
105114 * @return the instance
106115 */
107- @ NotNull
108- public static JavaFXImageManager getInstance () {
116+ public static @ NotNull JavaFXImageManager getInstance () {
109117 if (instance == null ) instance = new JavaFXImageManager ();
110118 return instance ;
111119 }
112120
121+ @ NotNull
122+ private IntegerDictionary <IntegerDictionary <ObjectDictionary <String , Image >>> smallImageCache ;
123+
113124 /**
114125 * The cache folder.
115126 */
@@ -122,21 +133,20 @@ private JavaFXImageManager() {
122133 final Path appFolder = Config .getAppFolderInUserHome ();
123134
124135 this .cacheFolder = appFolder .resolve (PREVIEW_CACHE_FOLDER );
125-
126- if (Files .exists (cacheFolder )) {
127- FileUtils .delete (cacheFolder );
128- }
136+ this .smallImageCache = DictionaryFactory .newIntegerDictionary ();
129137
130138 final ExecutorManager executorManager = ExecutorManager .getInstance ();
131139 executorManager .addFXTask (() -> FX_EVENT_MANAGER .addEventHandler (DeletedFileEvent .EVENT_TYPE ,
132140 event -> processEvent ((DeletedFileEvent ) event )));
141+ executorManager .addFXTask (() -> FX_EVENT_MANAGER .addEventHandler (ChangedCurrentAssetFolderEvent .EVENT_TYPE ,
142+ event -> processEvent ((ChangedCurrentAssetFolderEvent ) event )));
133143 }
134144
135145 /**
136146 * @return the cache folder.
137147 */
138- @ NotNull
139- private Path getCacheFolder () {
148+ @ FromAnyThread
149+ private @ NotNull Path getCacheFolder () {
140150 return cacheFolder ;
141151 }
142152
@@ -148,15 +158,68 @@ private Path getCacheFolder() {
148158 * @param height the required height.
149159 * @return the image.
150160 */
151- @ NotNull
152161 @ FXThread
153- public Image getImagePreview (@ Nullable final Path file , final int width , final int height ) {
162+ public @ NotNull Image getImagePreview (@ Nullable final Path file , final int width , final int height ) {
154163 if (file == null || !Files .exists (file )) return Icons .IMAGE_512 ;
155164
156- final FileTime lastModFile = Utils .get (file , first -> Files .getLastModifiedTime (first ));
157- final URL url = Utils .get (file , first -> first .toUri ().toURL ());
165+ if (width <= CACHED_IMAGES_SIZE && height <= CACHED_IMAGES_SIZE ) {
166+ final Image image = getFromCache (file .toString (), width , height );
167+ if (image != null ) return image ;
168+ }
169+
170+ final URL url = Utils .get (file , f -> f .toUri ().toURL ());
171+ final FileTime lastModFile = Utils .get (file , f -> Files .getLastModifiedTime (f ));
172+
173+ final Image image = getImagePreview (url , lastModFile , width , height );
174+
175+ if (width <= CACHED_IMAGES_SIZE && height <= CACHED_IMAGES_SIZE ) {
176+ putImageToCache (file .toString (), image , width , height );
177+ }
178+
179+ return image ;
180+ }
181+
182+ /**
183+ * Try to get an image from the cache by path and size.
184+ *
185+ * @param path the path to the image.
186+ * @param width the width.
187+ * @param height the height.
188+ * @return the image or null.
189+ */
190+ @ FXThread
191+ private @ Nullable Image getFromCache (@ NotNull final String path , final int width , final int height ) {
192+
193+ final IntegerDictionary <ObjectDictionary <String , Image >> heightImages = smallImageCache .get (width );
194+ if (heightImages == null ) {
195+ return null ;
196+ }
197+
198+ final ObjectDictionary <String , Image > images = heightImages .get (height );
199+ if (images == null ) {
200+ return null ;
201+ }
202+
203+ return images .get (path );
204+ }
205+
206+ /**
207+ * Put the image to the cache.
208+ *
209+ * @param path the ath to the image.
210+ * @param image the image.
211+ * @param width the width.
212+ * @param height the height.
213+ */
214+ @ FXThread
215+ private void putImageToCache (@ NotNull final String path , @ NotNull final Image image , final int width ,
216+ final int height ) {
217+
218+ final IntegerDictionary <ObjectDictionary <String , Image >> heightImages =
219+ smallImageCache .get (width , DictionaryFactory ::newIntegerDictionary );
158220
159- return getImagePreview (url , lastModFile , width , height );
221+ final ObjectDictionary <String , Image > images = heightImages .get (height , DictionaryFactory ::newObjectDictionary );
222+ images .put (path , image );
160223 }
161224
162225 /**
@@ -167,11 +230,42 @@ public Image getImagePreview(@Nullable final Path file, final int width, final i
167230 * @param height the required height.
168231 * @return the image.
169232 */
170- @ NotNull
171233 @ FXThread
172- public Image getImagePreview (@ Nullable final String resourcePath , final int width , final int height ) {
234+ public @ NotNull Image getImagePreview (@ Nullable final String resourcePath , final int width , final int height ) {
235+
236+ if (width <= CACHED_IMAGES_SIZE && height <= CACHED_IMAGES_SIZE ) {
237+ final Image image = getFromCache (resourcePath , width , height );
238+ if (image != null ) return image ;
239+ }
240+
241+ final Array <@ NotNull ClassLoader > classLoaders = ArrayFactory .newArray (ClassLoader .class );
242+ classLoaders .add (getClass ().getClassLoader ());
243+
244+ final ClasspathManager classpathManager = ClasspathManager .getInstance ();
245+ final URLClassLoader classesLoader = classpathManager .getClassesLoader ();
246+ final URLClassLoader librariesLoader = classpathManager .getLibrariesLoader ();
247+
248+ if (classesLoader != null ) {
249+ classLoaders .add (classesLoader );
250+ }
251+
252+ if (librariesLoader != null ) {
253+ classLoaders .add (librariesLoader );
254+ }
173255
174- URL url = getClass ().getResource (resourcePath );
256+ final PluginManager pluginManager = PluginManager .getInstance ();
257+ pluginManager .handlePlugins (plugin -> classLoaders .add (plugin .getClassLoader ()));
258+
259+ final String altResourcePath = "/" + resourcePath ;
260+
261+ URL url = null ;
262+
263+ for (final ClassLoader classLoader : classLoaders ) {
264+ url = classLoader .getResource (resourcePath );
265+ if (url != null ) break ;
266+ url = classLoader .getResource (altResourcePath );
267+ if (url != null ) break ;
268+ }
175269
176270 if (url == null ) {
177271 url = getClass ().getResource ("/" + resourcePath );
@@ -181,12 +275,18 @@ public Image getImagePreview(@Nullable final String resourcePath, final int widt
181275 return Icons .IMAGE_512 ;
182276 }
183277
184- return getImagePreview (url , null , width , height );
278+ final Image image = getImagePreview (url , null , width , height );
279+
280+ if (width <= CACHED_IMAGES_SIZE && height <= CACHED_IMAGES_SIZE ) {
281+ putImageToCache (resourcePath , image , width , height );
282+ }
283+
284+ return image ;
185285 }
186286
187- @ NotNull
188- private Image getImagePreview (@ NotNull URL url , @ Nullable final FileTime lastModFile ,
189- final int width , final int height ) {
287+ @ FXThread
288+ private @ NotNull Image getImagePreview (@ NotNull URL url , @ Nullable final FileTime lastModFile , final int width ,
289+ final int height ) {
190290
191291 final String externalForm = url .toExternalForm ();
192292 final String fileHash = StringUtils .toMD5 (externalForm ) + ".png" ;
@@ -252,6 +352,7 @@ private Image getImagePreview(@NotNull URL url, @Nullable final FileTime lastMod
252352 return Icons .IMAGE_512 ;
253353 }
254354
355+ @ FXThread
255356 private void writeDefaultToCache (@ NotNull final Path cacheFile ) {
256357 final BufferedImage bufferedImage = SwingFXUtils .fromFXImage (Icons .IMAGE_512 , null );
257358 try (final OutputStream out = Files .newOutputStream (cacheFile , WRITE , TRUNCATE_EXISTING , CREATE )) {
@@ -261,8 +362,9 @@ private void writeDefaultToCache(@NotNull final Path cacheFile) {
261362 }
262363 }
263364
264- @ NotNull
265- private Image readIOImage (@ NotNull final URL url , final int width , final int height , @ NotNull final Path cacheFile ) {
365+ @ FXThread
366+ private @ NotNull Image readIOImage (@ NotNull final URL url , final int width , final int height ,
367+ @ NotNull final Path cacheFile ) {
266368
267369 final BufferedImage read ;
268370 try {
@@ -275,9 +377,9 @@ private Image readIOImage(@NotNull final URL url, final int width, final int hei
275377 return scaleAndWrite (width , height , cacheFile , read , read .getWidth (), read .getHeight ());
276378 }
277379
278- @ NotNull
279- private Image readJMETexture (final int width , final int height , @ NotNull final String externalForm ,
280- @ NotNull final Path cacheFile ) {
380+ @ FXThread
381+ private @ NotNull Image readJMETexture (final int width , final int height , @ NotNull final String externalForm ,
382+ @ NotNull final Path cacheFile ) {
281383
282384 final Editor editor = Editor .getInstance ();
283385 final AssetManager assetManager = editor .getAssetManager ();
@@ -297,9 +399,9 @@ private Image readJMETexture(final int width, final int height, @NotNull final S
297399 return scaleAndWrite (width , height , cacheFile , textureImage , imageWidth , imageHeight );
298400 }
299401
300- @ NotNull
301- private Image readFXImage (final int width , final int height , @ NotNull final String externalForm ,
302- @ NotNull final Path cacheFile ) {
402+ @ FXThread
403+ private @ NotNull Image readFXImage (final int width , final int height , @ NotNull final String externalForm ,
404+ @ NotNull final Path cacheFile ) {
303405
304406 Image image = new Image (externalForm );
305407
@@ -329,8 +431,8 @@ private Image readFXImage(final int width, final int height, @NotNull final Stri
329431 return image ;
330432 }
331433
332- @ NotNull
333- private Image scaleAndWrite (final int targetWidth , final int targetHeight , @ NotNull final Path cacheFile ,
434+ @ FXThread
435+ private @ NotNull Image scaleAndWrite (final int targetWidth , final int targetHeight , @ NotNull final Path cacheFile ,
334436 @ NotNull final BufferedImage textureImage , final int currentWidth ,
335437 final int currentHeight ) {
336438
@@ -345,9 +447,9 @@ private Image scaleAndWrite(final int targetWidth, final int targetHeight, @NotN
345447 }
346448 }
347449
348- @ NotNull
349- private BufferedImage scaleImage (final int width , final int height , @ NotNull final BufferedImage read ,
350- final int imageWidth , final int imageHeight ) {
450+ @ FXThread
451+ private @ NotNull BufferedImage scaleImage (final int width , final int height , @ NotNull final BufferedImage read ,
452+ final int imageWidth , final int imageHeight ) {
351453
352454 java .awt .Image newImage = read ;
353455
@@ -372,7 +474,13 @@ private BufferedImage scaleImage(final int width, final int height, @NotNull fin
372474 return bufferedImage ;
373475 }
374476
477+ @ FXThread
375478 private void processEvent (@ NotNull final DeletedFileEvent event ) {
376479 //TODO need to add remove from cache
377480 }
481+
482+ @ FXThread
483+ private void processEvent (@ NotNull final ChangedCurrentAssetFolderEvent event ) {
484+ smallImageCache .clear ();
485+ }
378486}
0 commit comments