diff --git a/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/DecorationSnapshotBuilder.java b/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/DecorationSnapshotBuilder.java index f23bef3..988e9d2 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/DecorationSnapshotBuilder.java +++ b/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/DecorationSnapshotBuilder.java @@ -2,6 +2,7 @@ package eu.mhsl.minecraft.pixelpics.render.snapshot; import eu.mhsl.minecraft.pixelpics.render.entity.DecorationState; import eu.mhsl.minecraft.pixelpics.render.entity.ItemSpec; +import eu.mhsl.minecraft.pixelpics.utils.MapManager; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -51,9 +52,14 @@ public final class DecorationSnapshotBuilder { ItemStack stack = frame.getItem(); item = ItemSpecs.of(stack); if (stack.getType() == Material.FILLED_MAP) { - MapView view = MapPixels.view(stack); - map = MapPixels.of(view); - if (map != null) markers = MapPixels.markers(view); + int id = MapPixels.mapId(stack); + if (id >= 0 && MapManager.isManaged(id)) { + map = MapPixels.photo(id); // photos have no markers + } else { + MapView view = MapPixels.view(stack); + map = MapPixels.of(view); + if (map != null) markers = MapPixels.markers(view); + } } rotation = frame.getRotation().ordinal(); glow = e instanceof GlowItemFrame; diff --git a/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/MapPixels.java b/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/MapPixels.java index ec974aa..218fa0e 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/MapPixels.java +++ b/src/main/java/eu/mhsl/minecraft/pixelpics/render/snapshot/MapPixels.java @@ -36,22 +36,45 @@ final class MapPixels { } } + /** The stack's map id without loading its view, or -1. */ + static int mapId(ItemStack stack) { + try { + return stack.getItemMeta() instanceof MapMeta meta && meta.hasMapId() ? meta.getMapId() : -1; + } catch (Throwable t) { + return -1; + } + } + + /** + * A PixelPics photo ({@link MapManager#isManaged}) without loading the server's map data (a wall of photos not + * loaded yet cost ~4 ms per map on the main thread): its renderer's indices, else the stored index cache, else + * blank film like its map shows meanwhile. + */ + static int[][] photo(int mapId) { + ImageMapRenderer renderer = MapManager.renderer(mapId); + byte[] colors = renderer != null ? renderer.getIndices() : MapManager.readIndices(mapId); + return argb(colors != null ? colors : film()); + } + /** ARGB {@code [y][x]}, or null without map data (vanilla then draws the item). */ static int[][] of(MapView view) { if (view == null) return null; try { - byte[] colors = colors(view); - if (colors == null || colors.length != SIZE * SIZE) return null; - int[][] out = new int[SIZE][SIZE]; - for (int y = 0; y < SIZE; y++) { - for (int x = 0; x < SIZE; x++) out[y][x] = MapColorPalette.argb(colors[y * SIZE + x]); - } - return out; + return argb(colors(view)); } catch (Throwable t) { return null; } } + private static int[][] argb(byte[] colors) { + if (colors == null || colors.length != SIZE * SIZE) return null; + int[][] out = new int[SIZE][SIZE]; + for (int y = 0; y < SIZE; y++) { + for (int x = 0; x < SIZE; x++) out[y][x] = MapColorPalette.argb(colors[y * SIZE + x]); + } + return out; + } + private static byte[] colors(MapView view) throws ReflectiveOperationException { for (MapRenderer r : view.getRenderers()) { if (r instanceof ImageMapRenderer image) { diff --git a/src/main/java/eu/mhsl/minecraft/pixelpics/utils/MapManager.java b/src/main/java/eu/mhsl/minecraft/pixelpics/utils/MapManager.java index 37db535..078443a 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelpics/utils/MapManager.java +++ b/src/main/java/eu/mhsl/minecraft/pixelpics/utils/MapManager.java @@ -8,6 +8,7 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -37,6 +38,8 @@ public class MapManager { private static File folder; // Views carrying one of our renderers; weak, the server owns them. private static final Map attached = new WeakHashMap<>(); + // The renderer of each attached photo by map id (main thread), so a photo can be read without loading its view. + private static final Map renderers = new HashMap<>(); private MapManager() {} @@ -73,6 +76,12 @@ public class MapManager { mapView.addRenderer(renderer); mapView.setTrackingPosition(false); attached.put(mapView, Boolean.TRUE); + renderers.put(mapView.getId(), renderer); + } + + /** The renderer attached to photo map {@code id} in this session, or null when its view is not loaded (yet). */ + public static ImageMapRenderer renderer(int id) { + return renderers.get(id); } /** @@ -88,6 +97,7 @@ public class MapManager { ids.add(Integer.toString(view.getId())); } attached.clear(); + renderers.clear(); try { Files.write(new File(Main.getInstance().getDataFolder(), RELOAD_FILE).toPath(), ids); } catch (IOException e) {