From ba67740f344c3873879c51fab17b36b6d7fb70ca Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Mon, 7 Oct 2024 21:27:55 -0700 Subject: [PATCH] Make PNJ the default map image io backend Configs with (effectively) default image io settings will be switched to use PNJ. Otherwise, they will continue to use BufferedImage. Some basic testing shows that PNJ gives compression near the max setting for BufferedImage while taking less time. The following results are for rendering the same map twice in a row (after a warmup run) averaged over a couple of runs (time), and for the whole tiles directory for the dimension (size). Time is what was spent in `MapImage#save` on the image io thread as measured by spark (async profiler). PNJ : 724K in 1400ms BufferedImage compress-images=enabled, value=1 : 712K in 2300ms BufferedImage compress-images=disabled : 804K in 950ms --- common/build.gradle.kts | 2 + .../squaremap/common/config/Config.java | 39 +++++++++-- .../common/data/MapWorldInternal.java | 3 +- .../data/image/BufferedImageMapImageIO.java | 59 +++++++++++++++++ .../data/{Image.java => image/MapImage.java} | 65 ++++++------------- .../common/data/image/MapImageIO.java | 20 ++++++ .../common/data/image/PNJMapImageIO.java | 44 +++++++++++++ .../common/task/render/AbstractRender.java | 13 ++-- .../common/task/render/BackgroundRender.java | 5 +- .../common/task/render/RadiusRender.java | 5 +- .../common/util/ImageIOExecutor.java | 8 +-- gradle/libs.versions.toml | 2 + 12 files changed, 201 insertions(+), 64 deletions(-) create mode 100644 common/src/main/java/xyz/jpenilla/squaremap/common/data/image/BufferedImageMapImageIO.java rename common/src/main/java/xyz/jpenilla/squaremap/common/data/{Image.java => image/MapImage.java} (61%) create mode 100644 common/src/main/java/xyz/jpenilla/squaremap/common/data/image/MapImageIO.java create mode 100644 common/src/main/java/xyz/jpenilla/squaremap/common/data/image/PNJMapImageIO.java diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 55b4d04e..63c569bc 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -45,4 +45,6 @@ dependencies { api(libs.htmlSanitizerJ10) { isTransitive = false // depends on guava, provided by mc at runtime } + + implementation(libs.pnj) } diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/config/Config.java b/common/src/main/java/xyz/jpenilla/squaremap/common/config/Config.java index ba97a92e..e68f902d 100644 --- a/common/src/main/java/xyz/jpenilla/squaremap/common/config/Config.java +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/config/Config.java @@ -5,10 +5,13 @@ import org.spongepowered.configurate.NodePath; import org.spongepowered.configurate.transformation.ConfigurationTransformation; import xyz.jpenilla.squaremap.common.data.DirectoryProvider; +import xyz.jpenilla.squaremap.common.data.image.BufferedImageMapImageIO; +import xyz.jpenilla.squaremap.common.data.image.MapImageIO; +import xyz.jpenilla.squaremap.common.data.image.PNJMapImageIO; @SuppressWarnings("unused") public final class Config extends AbstractConfig { - private static final int LATEST_VERSION = 2; + private static final int LATEST_VERSION = 3; Config(final DirectoryProvider directoryProvider) { super(directoryProvider.dataDirectory(), Config.class, "config.yml", LATEST_VERSION); @@ -26,7 +29,26 @@ protected void addVersions(final ConfigurationTransformation.VersionedBuilder ve }) .build(); - versionedBuilder.addVersion(LATEST_VERSION, oneToTwo); + final ConfigurationTransformation twoToThree = ConfigurationTransformation.chain( + ConfigurationTransformation.builder() + .addAction(NodePath.path("settings", "image-quality"), (path, node) -> new Object[]{"settings", "image-io"}) + .build(), + ConfigurationTransformation.builder() + .addAction(NodePath.path("settings", "image-io", "compress-images"), (path, node) -> new Object[]{"settings", "image-io", "bufferedimage", "compress-images"}) + .build(), + ConfigurationTransformation.builder() + .addAction(NodePath.path("settings", "image-io"), (path, node) -> { + final boolean compress = node.node("bufferedimage", "compress-images", "enabled").getBoolean(); + if (compress) { + node.node("backend").set("bufferedimage"); + } + return null; + }) + .build() + ); + + versionedBuilder.addVersion(2, oneToTwo); + versionedBuilder.addVersion(LATEST_VERSION, twoToThree); } static Config config; @@ -59,11 +81,18 @@ private static void webDirSettings() { public static boolean COMPRESS_IMAGES = false; private static double COMPRESSION_RATIO_CONFIG = 0.0F; public static float COMPRESSION_RATIO; + public static MapImageIO MAP_IMAGE_IO; - private static void imageQualitySettings() { - COMPRESS_IMAGES = config.getBoolean("settings.image-quality.compress-images.enabled", COMPRESS_IMAGES); - COMPRESSION_RATIO_CONFIG = config.getDouble("settings.image-quality.compress-images.value", COMPRESSION_RATIO_CONFIG); + private static void imageIOSettings() { + COMPRESS_IMAGES = config.getBoolean("settings.image-io.bufferedimage.compress-images.enabled", COMPRESS_IMAGES); + COMPRESSION_RATIO_CONFIG = config.getDouble("settings.image-io.bufferedimage.compress-images.value", COMPRESSION_RATIO_CONFIG); COMPRESSION_RATIO = (float) (1.0D - COMPRESSION_RATIO_CONFIG); + final String imageIoBackend = config.getString("settings.image-io.backend", "pnj"); + MAP_IMAGE_IO = switch (imageIoBackend) { + case "bufferedimage" -> new BufferedImageMapImageIO(); + case "pnj" -> new PNJMapImageIO(); + default -> throw new IllegalArgumentException("Invaild image io backend: " + imageIoBackend + "; Supported options: [pnj, bufferedimage]"); + }; } public static boolean HTTPD_ENABLED = true; diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/data/MapWorldInternal.java b/common/src/main/java/xyz/jpenilla/squaremap/common/data/MapWorldInternal.java index 72c8d5bf..f2183230 100644 --- a/common/src/main/java/xyz/jpenilla/squaremap/common/data/MapWorldInternal.java +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/data/MapWorldInternal.java @@ -26,6 +26,7 @@ import xyz.jpenilla.squaremap.common.config.ConfigManager; import xyz.jpenilla.squaremap.common.config.WorldAdvanced; import xyz.jpenilla.squaremap.common.config.WorldConfig; +import xyz.jpenilla.squaremap.common.data.image.MapImage; import xyz.jpenilla.squaremap.common.layer.SpawnIconLayer; import xyz.jpenilla.squaremap.common.layer.WorldBorderLayer; import xyz.jpenilla.squaremap.common.task.render.RenderFactory; @@ -146,7 +147,7 @@ public int getMapColor(final BlockState state) { return Colors.rgb(state.getMapColor(null, null)); } - public void saveImage(final Image image) { + public void saveImage(final MapImage image) { this.imageIOExecutor.saveImage(image); } diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/BufferedImageMapImageIO.java b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/BufferedImageMapImageIO.java new file mode 100644 index 00000000..846157fa --- /dev/null +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/BufferedImageMapImageIO.java @@ -0,0 +1,59 @@ +package xyz.jpenilla.squaremap.common.data.image; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Path; +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; +import xyz.jpenilla.squaremap.common.config.Config; + +@DefaultQualifier(NonNull.class) +public final class BufferedImageMapImageIO implements MapImageIO { + @Override + public void save(final BufferedImageMapImage image, final OutputStream out) throws IOException { + final ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next(); + try (final ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(out)) { + writer.setOutput(imageOutputStream); + final ImageWriteParam param = writer.getDefaultWriteParam(); + if (Config.COMPRESS_IMAGES && param.canWriteCompressed()) { + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + if (param.getCompressionType() == null) { + param.setCompressionType(param.getCompressionTypes()[0]); + } + param.setCompressionQuality(Config.COMPRESSION_RATIO); + } + writer.write(null, new IIOImage(image.image(), null, null), param); + } + } + + @Override + public BufferedImageMapImage load(final Path input) throws IOException { + final @Nullable BufferedImage read = ImageIO.read(input.toFile()); + if (read == null) { + throw new IOException("Failed to read image file '" + input.toAbsolutePath() + "', ImageIO.read(File) result is null. This means no " + + "supported image format was able to read it. The image file may have been malformed or corrupted, it will be overwritten."); + } + return new BufferedImageMapImage(read); + } + + @Override + public BufferedImageMapImage newImage() { + return new BufferedImageMapImage( + new BufferedImage(MapImage.SIZE, MapImage.SIZE, BufferedImage.TYPE_INT_ARGB) + ); + } + + public record BufferedImageMapImage(BufferedImage image) implements MapImageIO.IOMapImage { + @Override + public void setPixel(final int x, final int y, final int color) { + this.image.setRGB(x, y, color); + } + } +} diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/data/Image.java b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/MapImage.java similarity index 61% rename from common/src/main/java/xyz/jpenilla/squaremap/common/data/Image.java rename to common/src/main/java/xyz/jpenilla/squaremap/common/data/image/MapImage.java index 39fc0f0f..4916e3d8 100644 --- a/common/src/main/java/xyz/jpenilla/squaremap/common/data/Image.java +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/MapImage.java @@ -1,40 +1,42 @@ -package xyz.jpenilla.squaremap.common.data; +package xyz.jpenilla.squaremap.common.data.image; import java.awt.Color; -import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; -import javax.imageio.IIOImage; -import javax.imageio.ImageIO; -import javax.imageio.ImageWriteParam; -import javax.imageio.ImageWriter; -import javax.imageio.stream.ImageOutputStream; import net.minecraft.util.Mth; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.framework.qual.DefaultQualifier; import xyz.jpenilla.squaremap.common.Logging; -import xyz.jpenilla.squaremap.common.config.Config; import xyz.jpenilla.squaremap.common.config.Messages; +import xyz.jpenilla.squaremap.common.data.RegionCoordinate; import xyz.jpenilla.squaremap.common.util.FileUtil; @DefaultQualifier(NonNull.class) -public final class Image { +public final class MapImage { private static final int TRANSPARENT = new Color(0, 0, 0, 0).getRGB(); public static final int SIZE = 512; + private final MapImageIO backend; private final RegionCoordinate region; private final Path directory; private final int maxZoom; private int @Nullable [][] pixels = null; - public Image(final RegionCoordinate region, final Path directory, final int maxZoom) { + @SuppressWarnings("unchecked") + public MapImage( + final RegionCoordinate region, + final Path directory, + final int maxZoom, + final MapImageIO backend + ) { this.region = region; this.directory = directory; this.maxZoom = maxZoom; + this.backend = (MapImageIO) backend; } public synchronized void setPixel(final int x, final int z, final int color) { @@ -59,7 +61,7 @@ public synchronized void save() { int scaledX = Mth.floor((double) this.region.x() / step); int scaledZ = Mth.floor((double) this.region.z() / step); - final BufferedImage image = this.getOrCreate(this.maxZoom - zoom, scaledX, scaledZ); + final MapImageIO.IOMapImage image = this.getOrCreate(this.maxZoom - zoom, scaledX, scaledZ); int baseX = (this.region.x() * size) & (SIZE - 1); int baseZ = (this.region.z() * size) & (SIZE - 1); @@ -68,29 +70,24 @@ public synchronized void save() { final int pixel = this.pixels[x][z]; if (pixel != Integer.MIN_VALUE) { final int color = pixel == 0 ? TRANSPARENT : pixel; - image.setRGB(baseX + (x / step), baseZ + (z / step), color); + image.setPixel(baseX + (x / step), baseZ + (z / step), color); } } } - this.save(this.maxZoom - zoom, scaledX, scaledZ, image); + this.saveImage(this.maxZoom - zoom, scaledX, scaledZ, image); } } - private BufferedImage getOrCreate(final int zoom, final int scaledX, final int scaledZ) { + private MapImageIO.IOMapImage getOrCreate(final int zoom, final int scaledX, final int scaledZ) { final Path file = this.imageInDirectory(zoom, scaledX, scaledZ); if (!Files.isRegularFile(file)) { - return newBufferedImage(); + return this.backend.newImage(); } try { - final @Nullable BufferedImage read = ImageIO.read(file.toFile()); - if (read == null) { - throw new IOException("Failed to read image file '" + file.toAbsolutePath() + "', ImageIO.read(File) result is null. This means no " + - "supported image format was able to read it. The image file may have been malformed or corrupted, it will be overwritten."); - } - return read; + return this.backend.load(file); } catch (final IOException ex) { try { Files.deleteIfExists(file); @@ -98,16 +95,16 @@ private BufferedImage getOrCreate(final int zoom, final int scaledX, final int s ex.addSuppressed(ex0); } this.logCouldNotRead(ex); - return newBufferedImage(); + return this.backend.newImage(); } } - private void save(final int zoom, final int scaledX, final int scaledZ, final BufferedImage image) { + private void saveImage(final int zoom, final int scaledX, final int scaledZ, final MapImageIO.IOMapImage image) { final Path out = this.imageInDirectory(zoom, scaledX, scaledZ); try { FileUtil.atomicWrite(out, tmp -> { try (final OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(tmp))) { - save(image, outputStream); + this.backend.save(image, outputStream); } }); } catch (final IOException ex) { @@ -115,22 +112,6 @@ private void save(final int zoom, final int scaledX, final int scaledZ, final Bu } } - private static void save(final BufferedImage image, final OutputStream out) throws IOException { - final ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next(); - try (final ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(out)) { - writer.setOutput(imageOutputStream); - final ImageWriteParam param = writer.getDefaultWriteParam(); - if (Config.COMPRESS_IMAGES && param.canWriteCompressed()) { - param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - if (param.getCompressionType() == null) { - param.setCompressionType(param.getCompressionTypes()[0]); - } - param.setCompressionQuality(Config.COMPRESSION_RATIO); - } - writer.write(null, new IIOImage(image, null, null), param); - } - } - private Path imageInDirectory(final int zoom, final int scaledX, final int scaledZ) { final Path dir = this.directory.resolve(Integer.toString(zoom)); if (!Files.exists(dir)) { @@ -144,10 +125,6 @@ private Path imageInDirectory(final int zoom, final int scaledX, final int scale return dir.resolve(fileName); } - private static BufferedImage newBufferedImage() { - return new BufferedImage(Image.SIZE, Image.SIZE, BufferedImage.TYPE_INT_ARGB); - } - private void logCouldNotRead(final IOException ex) { Logging.logger().error(xz(Messages.LOG_COULD_NOT_READ_REGION), ex); } diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/MapImageIO.java b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/MapImageIO.java new file mode 100644 index 00000000..6c355ed3 --- /dev/null +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/MapImageIO.java @@ -0,0 +1,20 @@ +package xyz.jpenilla.squaremap.common.data.image; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Path; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public interface MapImageIO { + void save(I image, OutputStream out) throws IOException; + + I load(Path input) throws IOException; + + I newImage(); + + interface IOMapImage { + void setPixel(int x, int y, int color); + } +} diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/PNJMapImageIO.java b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/PNJMapImageIO.java new file mode 100644 index 00000000..172dd5ee --- /dev/null +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/data/image/PNJMapImageIO.java @@ -0,0 +1,44 @@ +package xyz.jpenilla.squaremap.common.data.image; + +import io.github.xfacthd.pnj.api.PNJ; +import io.github.xfacthd.pnj.api.data.Image; +import io.github.xfacthd.pnj.api.define.ColorFormat; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Path; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public final class PNJMapImageIO implements MapImageIO { + + @Override + public void save(final PNJMapImage image, final OutputStream out) throws IOException { + PNJ.encode(out, image.image()); + } + + @Override + public PNJMapImage load(final Path input) throws IOException { + return new PNJMapImage(PNJ.decode(input)); + } + + @Override + public PNJMapImage newImage() { + return new PNJMapImage( + new Image( + MapImage.SIZE, + MapImage.SIZE, + ColorFormat.RGB_ALPHA, + 8, + new byte[MapImage.SIZE * MapImage.SIZE * 4] + ) + ); + } + + public record PNJMapImage(Image image) implements IOMapImage { + @Override + public void setPixel(int x, int y, int color) { + this.image.setPixel(x, y, color, true); + } + } +} diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/AbstractRender.java b/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/AbstractRender.java index c6628bd2..c375b2db 100644 --- a/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/AbstractRender.java +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/AbstractRender.java @@ -35,10 +35,11 @@ import org.checkerframework.framework.qual.DefaultQualifier; import xyz.jpenilla.squaremap.api.Pair; import xyz.jpenilla.squaremap.common.Logging; +import xyz.jpenilla.squaremap.common.config.Config; import xyz.jpenilla.squaremap.common.config.Messages; import xyz.jpenilla.squaremap.common.data.BiomeColors; import xyz.jpenilla.squaremap.common.data.ChunkCoordinate; -import xyz.jpenilla.squaremap.common.data.Image; +import xyz.jpenilla.squaremap.common.data.image.MapImage; import xyz.jpenilla.squaremap.common.data.MapWorldInternal; import xyz.jpenilla.squaremap.common.data.RegionCoordinate; import xyz.jpenilla.squaremap.common.util.ChunkHashMapKey; @@ -202,7 +203,7 @@ public final void restartProgressLogger() { } protected final void mapRegion(final RegionCoordinate region) { - final Image image = new Image(region, this.mapWorld.tilesPath(), this.mapWorld.config().ZOOM_MAX); + final MapImage image = new MapImage(region, this.mapWorld.tilesPath(), this.mapWorld.config().ZOOM_MAX, Config.MAP_IMAGE_IO); final int startX = region.getChunkX(); final int startZ = region.getChunkZ(); final List> futures = new ArrayList<>(); @@ -223,7 +224,7 @@ protected final void mapRegion(final RegionCoordinate region) { } } - protected final CompletableFuture mapSingleChunk(final Image image, final int chunkX, final int chunkZ) { + protected final CompletableFuture mapSingleChunk(final MapImage image, final int chunkX, final int chunkZ) { final CompletableFuture<@Nullable ChunkSnapshot> chunkFuture = this.chunks.snapshot(new ChunkPos(chunkX, chunkZ)); final CompletableFuture<@Nullable ChunkSnapshot> northChunk = this.chunks.snapshotDirect(new ChunkPos(chunkX, chunkZ - 1)); @@ -272,7 +273,7 @@ protected final CompletableFuture mapSingleChunk(final Image image, final }); } - protected final CompletableFuture mapChunkColumn(final Image image, final int chunkX, final int startChunkZ) { + protected final CompletableFuture mapChunkColumn(final MapImage image, final int chunkX, final int startChunkZ) { final List> futures = new ArrayList<>(33); final CompletableFuture<@Nullable ChunkSnapshot> aboveChunkFuture = this.chunks.snapshotDirect(new ChunkPos(chunkX, startChunkZ - 1)); @@ -309,7 +310,7 @@ protected final CompletableFuture mapChunkColumn(final Image image, final }); } - private void scanChunk(final Image image, final int[] lastY, final ChunkSnapshot chunk) { + private void scanChunk(final MapImage image, final int[] lastY, final ChunkSnapshot chunk) { while (this.mapWorld.renderManager().rendersPaused() && this.running()) { sleep(500); } @@ -327,7 +328,7 @@ private void scanChunk(final Image image, final int[] lastY, final ChunkSnapshot } } - private void scanTopRow(final Image image, final int[] lastY, final ChunkSnapshot chunk) { + private void scanTopRow(final MapImage image, final int[] lastY, final ChunkSnapshot chunk) { final int blockX = chunk.pos().getMinBlockX(); final int blockZ = chunk.pos().getMinBlockZ(); for (int x = 0; x < 16; x++) { diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/BackgroundRender.java b/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/BackgroundRender.java index 5dab097f..965065cd 100644 --- a/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/BackgroundRender.java +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/BackgroundRender.java @@ -16,8 +16,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.framework.qual.DefaultQualifier; import xyz.jpenilla.squaremap.common.Logging; +import xyz.jpenilla.squaremap.common.config.Config; import xyz.jpenilla.squaremap.common.data.ChunkCoordinate; -import xyz.jpenilla.squaremap.common.data.Image; +import xyz.jpenilla.squaremap.common.data.image.MapImage; import xyz.jpenilla.squaremap.common.data.MapWorldInternal; import xyz.jpenilla.squaremap.common.data.RegionCoordinate; import xyz.jpenilla.squaremap.common.util.Util; @@ -58,7 +59,7 @@ protected void render() { final Map> regionChunksMap = chunks.stream().collect(Collectors.groupingBy(ChunkCoordinate::regionCoordinate)); regionChunksMap.forEach((region, chunksToRenderInRegion) -> { - final Image image = new Image(region, this.mapWorld.tilesPath(), this.mapWorld.config().ZOOM_MAX); + final MapImage image = new MapImage(region, this.mapWorld.tilesPath(), this.mapWorld.config().ZOOM_MAX, Config.MAP_IMAGE_IO); final CompletableFuture[] chunkFutures = chunksToRenderInRegion.stream() .map(coord -> this.mapSingleChunk(image, coord.x(), coord.z())) diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/RadiusRender.java b/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/RadiusRender.java index a249a447..bf2b646f 100644 --- a/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/RadiusRender.java +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/task/render/RadiusRender.java @@ -14,9 +14,10 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.framework.qual.DefaultQualifier; import xyz.jpenilla.squaremap.common.Logging; +import xyz.jpenilla.squaremap.common.config.Config; import xyz.jpenilla.squaremap.common.config.Messages; import xyz.jpenilla.squaremap.common.data.ChunkCoordinate; -import xyz.jpenilla.squaremap.common.data.Image; +import xyz.jpenilla.squaremap.common.data.image.MapImage; import xyz.jpenilla.squaremap.common.data.MapWorldInternal; import xyz.jpenilla.squaremap.common.data.RegionCoordinate; import xyz.jpenilla.squaremap.common.util.Numbers; @@ -99,7 +100,7 @@ protected void render() { this.mapRegion(region); continue; } - final Image image = new Image(region, this.mapWorld.tilesPath(), this.mapWorld.config().ZOOM_MAX); + final MapImage image = new MapImage(region, this.mapWorld.tilesPath(), this.mapWorld.config().ZOOM_MAX, Config.MAP_IMAGE_IO); final List> chunkFutures = new ArrayList<>(); for (final ChunkCoordinate chunkCoord : chunkCoords) { chunkFutures.add(this.mapSingleChunk(image, chunkCoord.x(), chunkCoord.z())); diff --git a/common/src/main/java/xyz/jpenilla/squaremap/common/util/ImageIOExecutor.java b/common/src/main/java/xyz/jpenilla/squaremap/common/util/ImageIOExecutor.java index 1d69da3b..3ea963bc 100644 --- a/common/src/main/java/xyz/jpenilla/squaremap/common/util/ImageIOExecutor.java +++ b/common/src/main/java/xyz/jpenilla/squaremap/common/util/ImageIOExecutor.java @@ -8,7 +8,7 @@ import net.minecraft.server.level.ServerLevel; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.framework.qual.DefaultQualifier; -import xyz.jpenilla.squaremap.common.data.Image; +import xyz.jpenilla.squaremap.common.data.image.MapImage; @DefaultQualifier(NonNull.class) public final class ImageIOExecutor { @@ -25,14 +25,14 @@ private ImageIOExecutor(final ServerLevel level) { } /** - * Submits a save task for the given {@link Image} instance. If the save queue currently + * Submits a save task for the given {@link MapImage} instance. If the save queue currently * has {@link #IMAGE_IO_MAX_TASKS} or more tasks queued, this method will block until the queue * has less than {@link #IMAGE_IO_MAX_TASKS} tasks. This effectively throttles renders when the * save queue falls far behind a render, avoiding a potential memory leak. * - * @param image {@link Image} instance + * @param image {@link MapImage} instance */ - public void saveImage(final Image image) { + public void saveImage(final MapImage image) { this.submittedTasks.getAndIncrement(); this.executor.execute(() -> { try { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 075da5c8..7772d654 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -93,6 +93,8 @@ cardinalComponentsEntity = { group = "org.ladysnake.cardinal-components-api", na neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" } +pnj = "io.github.xfacthd:pnj:1.2" + # buildSrc indraCommon = { group = "net.kyori", name = "indra-common", version.ref = "indra" } indraPublishingSonatype = { group = "net.kyori", name = "indra-publishing-sonatype", version.ref = "indra" }