diff --git a/src/itdelatrisu/opsu/Container.java b/src/itdelatrisu/opsu/Container.java index e9ef3f5c..5fbb774b 100644 --- a/src/itdelatrisu/opsu/Container.java +++ b/src/itdelatrisu/opsu/Container.java @@ -126,7 +126,7 @@ public class Container extends AppGameContainer { // reset image references GameImage.clearReferences(); GameData.Grade.clearReferences(); - Beatmap.getBackgroundImageCache().clear(); + Beatmap.clearBackgroundImageCache(); // prevent loading tracks from re-initializing OpenAL MusicController.reset(); diff --git a/src/itdelatrisu/opsu/beatmap/Beatmap.java b/src/itdelatrisu/opsu/beatmap/Beatmap.java index 8e102e15..ea178513 100644 --- a/src/itdelatrisu/opsu/beatmap/Beatmap.java +++ b/src/itdelatrisu/opsu/beatmap/Beatmap.java @@ -23,9 +23,11 @@ import itdelatrisu.opsu.Options; import java.io.File; import java.util.ArrayList; import java.util.LinkedList; +import java.util.Map; import org.newdawn.slick.Color; import org.newdawn.slick.Image; +import org.newdawn.slick.SlickException; import org.newdawn.slick.util.Log; /** @@ -36,12 +38,28 @@ public class Beatmap implements Comparable { public static final byte MODE_OSU = 0, MODE_TAIKO = 1, MODE_CTB = 2, MODE_MANIA = 3; /** Background image cache. */ - private static final BeatmapImageCache bgImageCache = new BeatmapImageCache(); + @SuppressWarnings("serial") + private static final LRUCache bgImageCache = new LRUCache(10) { + @Override + public void eldestRemoved(Map.Entry eldest) { + Image img = eldest.getValue(); + if (img != null && !img.isDestroyed()) { + try { + img.destroy(); // destroy the removed image + } catch (SlickException e) { + Log.warn(String.format("Failed to destroy image '%s'.", img.getResourceReference()), e); + } + } + } + }; /** - * Returns the background image cache. + * Clears the background image cache. + *

+ * NOTE: This does NOT destroy the images in the cache, and will cause + * memory leaks if all images have not been destroyed. */ - public static BeatmapImageCache getBackgroundImageCache() { return bgImageCache; } + public static void clearBackgroundImageCache() { bgImageCache.clear(); } /** The OSU File object associated with this beatmap. */ private File file; @@ -269,7 +287,7 @@ public class Beatmap implements Comparable { Image bgImage = bgImageCache.get(this); if (bgImage == null) { bgImage = new Image(bg.getAbsolutePath()); - bgImageCache.put(this, bgImage); + bgImageCache.put(bg, bgImage); } int swidth = width; diff --git a/src/itdelatrisu/opsu/beatmap/BeatmapImageCache.java b/src/itdelatrisu/opsu/beatmap/BeatmapImageCache.java deleted file mode 100644 index fdf502f9..00000000 --- a/src/itdelatrisu/opsu/beatmap/BeatmapImageCache.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * opsu! - an open-source osu! client - * Copyright (C) 2014, 2015 Jeffrey Han - * - * opsu! is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * opsu! is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with opsu!. If not, see . - */ - -package itdelatrisu.opsu.beatmap; - -import java.io.File; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.newdawn.slick.Image; -import org.newdawn.slick.SlickException; -import org.newdawn.slick.util.Log; - -/** - * LRU cache for beatmap background images. - */ -public class BeatmapImageCache { - /** Maximum number of cached images. */ - private static final int MAX_CACHE_SIZE = 10; - - /** Map of all loaded background images. */ - private LinkedHashMap cache; - - /** - * Constructor. - */ - @SuppressWarnings("serial") - public BeatmapImageCache() { - this.cache = new LinkedHashMap(MAX_CACHE_SIZE + 1, 1.1f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - if (size() > MAX_CACHE_SIZE) { - // destroy the eldest image - Image img = eldest.getValue(); - if (img != null && !img.isDestroyed()) { - try { - img.destroy(); - } catch (SlickException e) { - Log.warn(String.format("Failed to destroy image '%s'.", img.getResourceReference()), e); - } - } - return true; - } - return false; - } - }; - } - - /** - * Returns the image mapped to the specified beatmap. - * @param beatmap the Beatmap - * @return the Image, or {@code null} if no such mapping exists - */ - public Image get(Beatmap beatmap) { return cache.get(beatmap.bg); } - - /** - * Creates a mapping from the specified beatmap to the given image. - * @param beatmap the Beatmap - * @param image the Image - * @return the previously mapped Image, or {@code null} if no such mapping existed - */ - public Image put(Beatmap beatmap, Image image) { return cache.put(beatmap.bg, image); } - - /** - * Removes all entries from the cache. - *

- * NOTE: This does NOT destroy the images in the cache, and will cause - * memory leaks if all images have not been destroyed. - */ - public void clear() { cache.clear(); } -} diff --git a/src/itdelatrisu/opsu/beatmap/LRUCache.java b/src/itdelatrisu/opsu/beatmap/LRUCache.java new file mode 100644 index 00000000..abd2b4b8 --- /dev/null +++ b/src/itdelatrisu/opsu/beatmap/LRUCache.java @@ -0,0 +1,59 @@ +/* + * opsu! - an open-source osu! client + * Copyright (C) 2014, 2015 Jeffrey Han + * + * opsu! is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * opsu! is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with opsu!. If not, see . + */ + +package itdelatrisu.opsu.beatmap; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Least recently used cache. + * + * @param the type of keys maintained by this map + * @param the type of mapped values + */ +@SuppressWarnings("serial") +public class LRUCache extends LinkedHashMap { + /** The cache capacity. */ + private final int capacity; + + /** + * Creates a least recently used cache with the given capacity. + * @param capacity the capacity + */ + public LRUCache(int capacity) { + super(capacity + 1, 1.1f, true); + this.capacity = capacity; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + if (size() > capacity) { + eldestRemoved(eldest); + return true; + } + return false; + } + + /** + * Notification that the eldest entry was removed. + * Can be used to clean up any resources when this happens (via override). + * @param eldest the removed entry + */ + public void eldestRemoved(Map.Entry eldest) {} +}