package net.querz.mcaselector.tiles; import javafx.application.Platform; import javafx.scene.image.Image; import net.querz.mcaselector.Config; import net.querz.mcaselector.debug.Debug; import net.querz.mcaselector.io.FileHelper; import net.querz.mcaselector.io.RegionImageGenerator; import net.querz.mcaselector.point.Point2i; import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class ImagePool { private final Map<Integer, LinkedHashMap<Point2i, Image>> pool = new HashMap<>(); private final Set<Point2i> noMCA = new HashSet<>(); private final TileMap tileMap; private final double poolSize; // poolSize is a percentage indicating the amount of images cached in relation to the visible region public ImagePool(TileMap tileMap, double poolSize) { // initialize pool int maxZoomLevel = Config.getMaxZoomLevel(); for (int i = 0; i < maxZoomLevel; i++) { pool.put((int) Math.pow(2, i), new LinkedHashMap<>()); } this.tileMap = tileMap; this.poolSize = poolSize; } public void requestImage(Tile tile, int scale) { if (noMCA.contains(tile.location)) { return; } tile.setLoading(true); // check if image exists in pool Image img = pool.get(scale).get(tile.location); if (img != null) { Debug.dumpf("image was cached in image pool: %d/%s", scale, tile.location); tile.setImage(img); tile.setLoaded(true); tile.setLoading(false); return; } // check if image exists in cache File cachedImgFile = FileHelper.createPNGFilePath(Config.getCacheDir(), scale, tile.location); if (cachedImgFile.exists()) { // load cached file Image cachedImg = new Image(cachedImgFile.toURI().toString(), true); cachedImg.progressProperty().addListener((v, o, n) -> { if (n.intValue() == 1 && !cachedImg.isError()) { Debug.dump("image loaded: " + cachedImgFile.getAbsolutePath()); tile.setImage(cachedImg); tile.setLoaded(true); tile.setLoading(false); push(scale, tile.location, cachedImg); tileMap.update(); } }); } else { Debug.dump("image does not exist: " + cachedImgFile.getAbsolutePath()); RegionImageGenerator.generate(tile, Config.getWorldUUID(), (i, u) -> { if (i == null) { noMCA.add(tile.location); return; } synchronized (Config.getWorldUUID()) { if (u.equals(Config.getWorldUUID())) { push(scale, tile.location, i); Platform.runLater(tileMap::update); } } }, () -> (float) scale, false, null); } } private void push(int scale, Point2i location, Image img) { pool.get(scale).put(location, img); trim(scale); } private void trim(int scale) { LinkedHashMap<Point2i, Image> scaleEntry = pool.get(scale); if (scaleEntry.size() <= tileMap.getVisibleTiles() * poolSize) { return; } Iterator<Point2i> it = scaleEntry.keySet().iterator(); while (it.hasNext() && scaleEntry.size() > tileMap.getVisibleTiles() * poolSize) { it.next(); it.remove(); } } public void clear() { for (Map.Entry<Integer, LinkedHashMap<Point2i, Image>> scale : pool.entrySet()) { scale.getValue().clear(); } clearNoMCACache(); } public void discardImage(Point2i region) { for (Map.Entry<Integer, LinkedHashMap<Point2i, Image>> scale : pool.entrySet()) { scale.getValue().remove(region); } noMCA.remove(region); } public void clearNoMCACache() { noMCA.clear(); } }