From 3a064a5ffa9d62f1ff89246d1618f44e7a18258c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Thu, 5 Nov 2026 17:03:47 +0100 Subject: [PATCH] photos in item frames are read from their renderer or index cache without loading the map view (a wall of 1000 frames with not yet loaded photos took 830 ms on the main thread, now 490 ms cold / 60 ms warm) --- .../snapshot/DecorationSnapshotBuilder.java | 12 ++++-- .../pixelpics/render/snapshot/MapPixels.java | 37 +++++++++++++++---- .../minecraft/pixelpics/utils/MapManager.java | 10 +++++ 3 files changed, 49 insertions(+), 10 deletions(-) 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) {