From 305110d890837fb9e3cb4cf835c62d2cc37a00f9 Mon Sep 17 00:00:00 2001 From: HyperPigeon <58276133+HyperPigeon@users.noreply.github.com> Date: Thu, 31 Mar 2022 18:52:47 -0400 Subject: [PATCH] -updated to 1.18.2 - made jank version of Image2Map in 1.18.2 --- build.gradle | 8 +- gradle.properties | 8 +- .../net/hyper_pigeon/image2map/Image2Map.java | 52 +++++ .../image2map/renderer/MapRenderer.java | 206 ++++++++++++++++++ .../polaroidcamera/PolaroidCamera.java | 10 +- .../client/render/CameraScreen.java | 6 +- src/main/resources/fabric.mod.json | 2 +- 7 files changed, 275 insertions(+), 17 deletions(-) create mode 100644 src/main/java/net/hyper_pigeon/image2map/Image2Map.java create mode 100644 src/main/java/net/hyper_pigeon/image2map/renderer/MapRenderer.java diff --git a/build.gradle b/build.gradle index 1916fcb..6025d08 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '0.10-SNAPSHOT' + id 'fabric-loom' version '0.12-SNAPSHOT' id 'maven-publish' id "com.jfrog.artifactory" version "4.15.2" } @@ -49,9 +49,9 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - modImplementation "curse.maven:image2map-403656:3384105" - modApi "curse.maven:image2map-403656:3384105" - include "curse.maven:image2map-403656:3384105" +// modImplementation "curse.maven:image2map-403656:3384105" +// modApi "curse.maven:image2map-403656:3384105" +// include "curse.maven:image2map-403656:3384105" // Config library modApi ("me.shedaniel.cloth:cloth-config-fabric:4.11.26") { exclude module: 'fabric-api' } diff --git a/gradle.properties b/gradle.properties index 6f0681d..1a95874 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,13 +2,13 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=1.17.1 -yarn_mappings=1.17.1+build.65 +minecraft_version=1.18.2 +yarn_mappings=1.18.2+build.2 loader_version=0.13.3 # Mod Properties -mod_version=1.1.1 +mod_version=1.2.0 maven_group=net.hyper_pigeon archives_base_name=polaroidcamera # Dependencies # check this on https://modmuss50.me/fabric.html -fabric_version=0.46.1+1.17 +fabric_version=0.48.0+1.18.2 diff --git a/src/main/java/net/hyper_pigeon/image2map/Image2Map.java b/src/main/java/net/hyper_pigeon/image2map/Image2Map.java new file mode 100644 index 0000000..4eb2963 --- /dev/null +++ b/src/main/java/net/hyper_pigeon/image2map/Image2Map.java @@ -0,0 +1,52 @@ +package net.hyper_pigeon.image2map; + +//https://github.com/TheEssem/Image2Map + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.LiteralText; +import net.minecraft.util.math.Vec3d; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.CompletableFuture; + +public class Image2Map { + class DitherModeSuggestionProvider implements SuggestionProvider { + + @Override + public CompletableFuture getSuggestions(CommandContext context, + SuggestionsBuilder builder) throws CommandSyntaxException { + builder.suggest("none"); + builder.suggest("dither"); + return builder.buildFuture(); + } + + } + + public enum DitherMode { + NONE, + FLOYD; + + public static DitherMode fromString(String string) { + if (string.equalsIgnoreCase("NONE")) + return DitherMode.NONE; + else if (string.equalsIgnoreCase("DITHER") || string.equalsIgnoreCase("FLOYD")) + return DitherMode.FLOYD; + throw new IllegalArgumentException("invalid dither mode"); + } + } + + + + +} diff --git a/src/main/java/net/hyper_pigeon/image2map/renderer/MapRenderer.java b/src/main/java/net/hyper_pigeon/image2map/renderer/MapRenderer.java new file mode 100644 index 0000000..773dd3b --- /dev/null +++ b/src/main/java/net/hyper_pigeon/image2map/renderer/MapRenderer.java @@ -0,0 +1,206 @@ +package net.hyper_pigeon.image2map.renderer; + +import net.hyper_pigeon.image2map.Image2Map; +import net.minecraft.block.MapColor; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.FilledMapItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.map.MapState; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.server.world.ServerWorld; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.util.Arrays; +import java.util.Objects; + +public class MapRenderer { + + + + private static final double shadeCoeffs[] = { 0.71, 0.86, 1.0, 0.53 }; + + public static MapColor[] getColors(){ + MapColor[] colors = new MapColor[64]; + for (int i = 0; i<= 63; i++){ + colors[i] = MapColor.get(i); + } + return colors; + } + + + private static double distance(double[] vectorA, double[] vectorB) { + return Math.sqrt(Math.pow(vectorA[0] - vectorB[0], 2) + Math.pow(vectorA[1] - vectorB[1], 2) + + Math.pow(vectorA[2] - vectorB[2], 2)); + } + + private static double[] applyShade(double[] color, int ind) { + double coeff = shadeCoeffs[ind]; + return new double[] { color[0] * coeff, color[1] * coeff, color[2] * coeff }; + } + + public static ItemStack render(BufferedImage image, Image2Map.DitherMode mode, ServerWorld world, double x, double z, + PlayerEntity player) { + // mojang removed the ability to set a map as locked via the "locked" field in + // 1.17, so we create and apply our own MapState instead + ItemStack stack = new ItemStack(Items.FILLED_MAP); + int id = world.getNextMapId(); + NbtCompound nbt = new NbtCompound(); + + nbt.putString("dimension", world.getRegistryKey().getValue().toString()); + nbt.putInt("xCenter", (int) x); + nbt.putInt("zCenter", (int) z); + nbt.putBoolean("locked", true); + nbt.putBoolean("unlimitedTracking", false); + nbt.putBoolean("trackingPosition", false); + nbt.putByte("scale", (byte) 3); + MapState state = MapState.fromNbt(nbt); + world.putMapState(FilledMapItem.getMapName(id), state); + stack.getOrCreateNbt().putInt("map", id); + + Image resizedImage = image.getScaledInstance(128, 128, Image.SCALE_DEFAULT); + BufferedImage resized = convertToBufferedImage(resizedImage); + int width = resized.getWidth(); + int height = resized.getHeight(); + int[][] pixels = convertPixelArray(resized); + MapColor[] mapColors = getColors(); + Color imageColor; + mapColors = Arrays.stream(mapColors).filter(Objects::nonNull).toArray(MapColor[]::new); + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + imageColor = new Color(pixels[j][i], true); + if (mode.equals(Image2Map.DitherMode.FLOYD)) + state.colors[i + j * width] = (byte) floydDither(mapColors, pixels, i, j, imageColor); + else + state.colors[i + j * width] = (byte) nearestColor(mapColors, imageColor); + } + } + return stack; + } + + private static Color mapColorToRGBColor(MapColor[] colors, int color) { + Color mcColor = new Color(colors[color >> 2].color); + double[] mcColorVec = { (double) mcColor.getRed(), (double) mcColor.getGreen(), (double) mcColor.getBlue() }; + double coeff = shadeCoeffs[color & 3]; + return new Color((int) (mcColorVec[0] * coeff), (int) (mcColorVec[1] * coeff), (int) (mcColorVec[2] * coeff)); + } + + private static class NegatableColor { + public final int r; + public final int g; + public final int b; + + public NegatableColor(int r, int g, int b) { + this.r = r; + this.g = g; + this.b = b; + } + } + + private static int floydDither(MapColor[] mapColors, int[][] pixels, int x, int y, Color imageColor) { + // double[] imageVec = { (double) imageColor.getRed() / 255.0, (double) + // imageColor.getGreen() / 255.0, + // (double) imageColor.getBlue() / 255.0 }; + int colorIndex = nearestColor(mapColors, imageColor); + Color palletedColor = mapColorToRGBColor(mapColors, colorIndex); + NegatableColor error = new NegatableColor(imageColor.getRed() - palletedColor.getRed(), + imageColor.getGreen() - palletedColor.getGreen(), imageColor.getBlue() - palletedColor.getBlue()); + if (pixels[0].length > x + 1) { + Color pixelColor = new Color(pixels[y][x + 1], true); + pixels[y][x + 1] = applyError(pixelColor, error, 7.0 / 16.0); + } + if (pixels.length > y + 1) { + if (x > 0) { + Color pixelColor = new Color(pixels[y + 1][x - 1], true); + pixels[y + 1][x - 1] = applyError(pixelColor, error, 3.0 / 16.0); + } + Color pixelColor = new Color(pixels[y + 1][x], true); + pixels[y + 1][x] = applyError(pixelColor, error, 5.0 / 16.0); + if (pixels[0].length > x + 1) { + pixelColor = new Color(pixels[y + 1][x + 1], true); + pixels[y + 1][x + 1] = applyError(pixelColor, error, 1.0 / 16.0); + } + } + + return colorIndex; + } + + private static int applyError(Color pixelColor, NegatableColor error, double quantConst) { + int pR = clamp(pixelColor.getRed() + (int) ((double) error.r * quantConst), 0, 255); + int pG = clamp(pixelColor.getGreen() + (int) ((double) error.g * quantConst), 0, 255); + int pB = clamp(pixelColor.getBlue() + (int) ((double) error.b * quantConst), 0, 255); + return new Color(pR, pG, pB, pixelColor.getAlpha()).getRGB(); + } + + private static int clamp(int i, int min, int max) { + if (min > max) + throw new IllegalArgumentException("max value cannot be less than min value"); + if (i < min) + return min; + if (i > max) + return max; + return i; + } + + private static int nearestColor(MapColor[] colors, Color imageColor) { + double[] imageVec = { (double) imageColor.getRed() / 255.0, (double) imageColor.getGreen() / 255.0, + (double) imageColor.getBlue() / 255.0 }; + int best_color = 0; + double lowest_distance = 10000; + for (int k = 0; k < colors.length; k++) { + Color mcColor = new Color(colors[k].color); + double[] mcColorVec = { (double) mcColor.getRed() / 255.0, (double) mcColor.getGreen() / 255.0, + (double) mcColor.getBlue() / 255.0 }; + for (int shadeInd = 0; shadeInd < shadeCoeffs.length; shadeInd++) { + double distance = distance(imageVec, applyShade(mcColorVec, shadeInd)); + if (distance < lowest_distance) { + lowest_distance = distance; + // todo: handle shading with alpha values other than 255 + if (k == 0 && imageColor.getAlpha() == 255) { + best_color = 119; + } else { + best_color = k * shadeCoeffs.length + shadeInd; + } + } + } + } + return best_color; + } + + private static int[][] convertPixelArray(BufferedImage image) { + + final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); + final int width = image.getWidth(); + final int height = image.getHeight(); + + int[][] result = new int[height][width]; + final int pixelLength = 4; + for (int pixel = 0, row = 0, col = 0; pixel + 3 < pixels.length; pixel += pixelLength) { + int argb = 0; + argb += (((int) pixels[pixel] & 0xff) << 24); // alpha + argb += ((int) pixels[pixel + 1] & 0xff); // blue + argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green + argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red + result[row][col] = argb; + col++; + if (col == width) { + col = 0; + row++; + } + } + + return result; + } + + private static BufferedImage convertToBufferedImage(Image image) { + BufferedImage newImage = new BufferedImage(image.getWidth(null), image.getHeight(null), + BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D g = newImage.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return newImage; + } +} \ No newline at end of file diff --git a/src/main/java/net/hyper_pigeon/polaroidcamera/PolaroidCamera.java b/src/main/java/net/hyper_pigeon/polaroidcamera/PolaroidCamera.java index 1b2e569..98660c3 100644 --- a/src/main/java/net/hyper_pigeon/polaroidcamera/PolaroidCamera.java +++ b/src/main/java/net/hyper_pigeon/polaroidcamera/PolaroidCamera.java @@ -2,6 +2,8 @@ import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.hyper_pigeon.image2map.Image2Map; +import net.hyper_pigeon.image2map.renderer.MapRenderer; import net.hyper_pigeon.polaroidcamera.items.CameraItem; import net.hyper_pigeon.polaroidcamera.networking.PolaroidCameraNetworkingConstants; import net.hyper_pigeon.polaroidcamera.persistent_state.ImagePersistentState; @@ -12,10 +14,8 @@ import net.minecraft.item.Items; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; -import net.minecraft.util.TypedActionResult; import net.minecraft.util.registry.Registry; -import space.essem.image2map.Image2Map; -import space.essem.image2map.renderer.MapRenderer; + import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -38,7 +38,7 @@ public void onInitialize() { { String identifier = buf.readString(); byte[] bytes = buf.readByteArray(); - ImagePersistentState imagePersistentState = ImagePersistentState.get(player.getServerWorld()); + ImagePersistentState imagePersistentState = ImagePersistentState.get(player.getWorld()); server.execute(() -> { if (!imagePersistentState.containsID(identifier)){ imagePersistentState.addByteArray(identifier,bytes); @@ -60,7 +60,7 @@ public void onInitialize() { server.execute(() -> { if(player.getInventory().contains(new ItemStack(Items.MAP)) || player.isCreative()) { try { - BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(ImagePersistentState.get(player.getServerWorld()).getByteArray(identifier))); + BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(ImagePersistentState.get(player.getWorld()).getByteArray(identifier))); bufferedImage = this.crop(bufferedImage,1080,1080); ItemStack mapItemStack = MapRenderer.render(bufferedImage, Image2Map.DitherMode.FLOYD,(ServerWorld) player.getEntityWorld(), diff --git a/src/main/java/net/hyper_pigeon/polaroidcamera/client/render/CameraScreen.java b/src/main/java/net/hyper_pigeon/polaroidcamera/client/render/CameraScreen.java index e1eacbb..9a75e51 100644 --- a/src/main/java/net/hyper_pigeon/polaroidcamera/client/render/CameraScreen.java +++ b/src/main/java/net/hyper_pigeon/polaroidcamera/client/render/CameraScreen.java @@ -102,13 +102,13 @@ public boolean keyPressed(int keyCode, int scanCode, int modifiers){ } - public boolean isPauseScreen() { + public boolean shouldPause() { return false; } - public void onClose() { + public void close() { this.client.options.hudHidden = false; this.client.options.fov = defaultFOV; - super.onClose(); + super.close(); } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index f42f0cb..4430b4f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -23,6 +23,6 @@ "depends": { "fabricloader": ">=0.13.3", "fabric": "*", - "minecraft": "1.17.1" + "minecraft": "1.18.x" } }