diff --git a/src/main/java/org/cyclops/iconexporter/client/gui/IExportTask.java b/src/main/java/org/cyclops/iconexporter/client/gui/IExportTask.java index 86e27e8..e0c5a32 100644 --- a/src/main/java/org/cyclops/iconexporter/client/gui/IExportTask.java +++ b/src/main/java/org/cyclops/iconexporter/client/gui/IExportTask.java @@ -1,7 +1,10 @@ package org.cyclops.iconexporter.client.gui; import com.mojang.blaze3d.matrix.MatrixStack; +import net.minecraft.client.renderer.texture.NativeImage; +import org.cyclops.cyclopscore.datastructure.Wrapper; +import javax.annotation.Nullable; import java.io.IOException; /** @@ -9,6 +12,6 @@ */ public interface IExportTask { - public void run(MatrixStack matrixStack) throws IOException; + public void run(MatrixStack matrixStack, Wrapper bImage) throws IOException; } diff --git a/src/main/java/org/cyclops/iconexporter/client/gui/ImageExportUtil.java b/src/main/java/org/cyclops/iconexporter/client/gui/ImageExportUtil.java index a941093..d3cb4ce 100644 --- a/src/main/java/org/cyclops/iconexporter/client/gui/ImageExportUtil.java +++ b/src/main/java/org/cyclops/iconexporter/client/gui/ImageExportUtil.java @@ -18,28 +18,54 @@ * @author rubensworks */ public class ImageExportUtil { - - public static void exportImageFromScreenshot(File dir, String key, int guiWidth, int guiHeight, int scaleImage, int backgroundColor) throws IOException { - // Take a screenshot + public static NativeImage takeScreenshot(int guiWidth, int guiHeight, int scaleImage) { NativeImage image = ScreenShotHelper.createScreenshot(guiWidth, guiHeight, Minecraft.getInstance().getFramebuffer()); - image = getSubImage(image, scaleImage, scaleImage); + return getSubImage(image, scaleImage, scaleImage); + } - // Convert our background color to a fully transparent pixel + // For opaque items/fluids, replaces background color with fully transparent pixels + public static NativeImage adjustImageAlpha(NativeImage image, int bgColor) { byte alpha = (byte) 256; alpha %= 0xff; for (int cx = 0; cx < image.getWidth(); cx++) { for (int cy = 0; cy < image.getHeight(); cy++) { int color = image.getPixelRGBA(cx, cy); - if (color == backgroundColor) { + if (color == bgColor) { color = 0; int mc = (alpha << 24) | 0x00ffffff; - int newcolor = color & mc; - image.setPixelRGBA(cx, cy, newcolor); + int newColor = color & mc; + image.setPixelRGBA(cx, cy, newColor); } } } + return image; + } + + // For non-opaque items/fluids, calculate alpha and adjust image accordingly + public static NativeImage adjustImageAlpha(NativeImage blackImage, NativeImage whiteImage) { + for (int cx = 0; cx < blackImage.getWidth(); cx++) { + for (int cy = 0; cy < blackImage.getHeight(); cy++) { + int blackTinted = blackImage.getPixelRGBA(cx, cy); + int whiteTinted = whiteImage.getPixelRGBA(cx, cy); + short alpha = (short) (255 + (blackTinted & 0xff) - (whiteTinted & 0xff)); + if (alpha == 0) { + blackImage.setPixelRGBA(cx, cy, blackTinted & 0x00ffffff); + } + else if (alpha > 0 && alpha < 255) { + int red = (255 * ((blackTinted & 0xff0000) >> 16) / alpha) & 0xff; + int blue = (255 * ((blackTinted & 0xff00) >> 8) / alpha) & 0xff; + int green = (255 * (blackTinted & 0xff) / alpha) & 0xff; + int newColor = (alpha << 24) | (red << 16) | (blue << 8) | (green); + blackImage.setPixelRGBA(cx, cy, newColor); + } + } + } + whiteImage.close(); + return blackImage; + } + public static void exportImage(File dir, String key, NativeImage image) throws IOException { // Write the file key = key .replaceAll(":", "__") diff --git a/src/main/java/org/cyclops/iconexporter/client/gui/ScreenIconExporter.java b/src/main/java/org/cyclops/iconexporter/client/gui/ScreenIconExporter.java index 3896e47..725586e 100644 --- a/src/main/java/org/cyclops/iconexporter/client/gui/ScreenIconExporter.java +++ b/src/main/java/org/cyclops/iconexporter/client/gui/ScreenIconExporter.java @@ -4,7 +4,11 @@ import com.mojang.blaze3d.matrix.MatrixStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderTypeLookup; +import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.fluid.Fluid; +import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemStack; @@ -20,10 +24,14 @@ import org.cyclops.cyclopscore.helper.Helpers; import org.cyclops.iconexporter.GeneralConfig; +import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.Queue; +import java.util.function.Predicate; + +import static org.cyclops.iconexporter.client.gui.ImageExportUtil.takeScreenshot; /** * A temporary gui for exporting icons. @@ -34,17 +42,27 @@ */ public class ScreenIconExporter extends Screen { - private static final int BACKGROUND_COLOR = Helpers.RGBAToInt(1, 0, 0, 255); // -16711680 - private static final int BACKGROUND_COLOR_SHIFTED = -16777215; // For some reason, MC shifts around colors internally... (R seems to be moved from the 16th bit to the 0th bit) + private static final int BLACK = Helpers.RGBAToInt(0, 0, 0, 255); // -16777216 + private static final int WHITE = Helpers.RGBAToInt(255, 255, 255, 255); // -1 + private static final int NOT_BLACK = Helpers.RGBAToInt(1, 0, 0, 255); // -16711680 + private static final int NOT_BLACK_SHIFTED = -16777215; // For some reason, MC shifts around colors internally... (R seems to be moved from the 16th bit to the 0th bit) private final int scaleImage; private final double scaleGui; + private final Predicate filter; private final Queue exportTasks; + private Wrapper blackImage = new Wrapper<>(); - public ScreenIconExporter(int scaleImage, double scaleGui) { + public ScreenIconExporter(int scaleImage, double scaleGui, @Nullable String namespace) { super(new TranslationTextComponent("gui.itemexporter.name")); this.scaleImage = scaleImage; this.scaleGui = scaleGui; + if (namespace == null) { + filter = (key) -> true; + } + else { + filter = (key) -> key.getNamespace().equals(namespace); + } this.exportTasks = this.createExportTasks(); } @@ -58,7 +76,7 @@ public void render(MatrixStack matrixStack, int mouseX, int mouseY, float partia } else { IExportTask task = exportTasks.poll(); try { - task.run(matrixStack); + task.run(matrixStack, blackImage); } catch (IOException e) { Minecraft.getInstance().player.sendMessage(new TranslationTextComponent("gui.itemexporter.error"), Util.DUMMY_UUID); e.printStackTrace(); @@ -89,35 +107,79 @@ public Queue createExportTasks() { // Add fluids for (Map.Entry, Fluid> fluidEntry : ForgeRegistries.FLUIDS.getEntries()) { - tasks.set(tasks.get() + 1); - String subKey = "fluid:" + fluidEntry.getKey().getLocation(); - exportTasks.add((matrixStack) -> { - taskProcessed.set(taskProcessed.get() + 1); - signalStatus(tasks, taskProcessed); - fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, BACKGROUND_COLOR); - ItemRenderUtil.renderFluid(this, matrixStack, fluidEntry.getValue(), scaleModified); - ImageExportUtil.exportImageFromScreenshot(baseDir, subKey, this.width, this.height, this.scaleImage, BACKGROUND_COLOR_SHIFTED); - }); + ResourceLocation location = fluidEntry.getKey().getLocation(); + if (filter.test(location)) { + tasks.set(tasks.get() + 1); + String subKey = "fluid:" + location; + //Test if fluid is opaque + if (RenderTypeLookup.canRenderInLayer(fluidEntry.getValue().getDefaultState(), RenderType.getSolid())) { + exportTasks.add((matrixStack, bImage) -> { + taskProcessed.set(taskProcessed.get() + 1); + signalStatus(tasks, taskProcessed); + fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, NOT_BLACK); + ItemRenderUtil.renderFluid(this, matrixStack, fluidEntry.getValue(), scaleModified); + NativeImage image = ImageExportUtil.takeScreenshot(this.width, this.height, this.scaleImage); + ImageExportUtil.exportImage(baseDir, subKey, ImageExportUtil.adjustImageAlpha(image, NOT_BLACK_SHIFTED)); + }); + } else { + exportTasks.add((matrixStack, bImage) -> { + fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, BLACK); + ItemRenderUtil.renderFluid(this, matrixStack, fluidEntry.getValue(), scaleModified); + bImage.set(takeScreenshot(this.width, this.height, this.scaleImage)); + }); + exportTasks.add((matrixStack, bImage) -> { + taskProcessed.set(taskProcessed.get() + 1); + signalStatus(tasks, taskProcessed); + fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, WHITE); + ItemRenderUtil.renderFluid(this, matrixStack, fluidEntry.getValue(), scaleModified); + NativeImage wImage = takeScreenshot(this.width, this.height, this.scaleImage); + ImageExportUtil.exportImage(baseDir, subKey, ImageExportUtil.adjustImageAlpha(bImage.get(), wImage)); + }); + } + } } // Add items for (ResourceLocation key : ForgeRegistries.ITEMS.getKeys()) { - Item value = ForgeRegistries.ITEMS.getValue(key); - NonNullList subItems = NonNullList.create(); - value.fillItemGroup(ItemGroup.SEARCH, subItems); - for (ItemStack subItem : subItems) { - tasks.set(tasks.get() + 1); - String subKey = key + (subItem.hasTag() ? "__" + serializeNbtTag(subItem.getTag()) : ""); - exportTasks.add((matrixStack) -> { - taskProcessed.set(taskProcessed.get() + 1); - signalStatus(tasks, taskProcessed); - fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, BACKGROUND_COLOR); - ItemRenderUtil.renderItem(subItem, scaleModified); - ImageExportUtil.exportImageFromScreenshot(baseDir, subKey, this.width, this.height, this.scaleImage, BACKGROUND_COLOR_SHIFTED); - if (subItem.hasTag() && GeneralConfig.fileNameHashTag) { - ImageExportUtil.exportNbtFile(baseDir, subKey, subItem.getTag()); + if (filter.test(key)) { + Item value = ForgeRegistries.ITEMS.getValue(key); + NonNullList subItems = NonNullList.create(); + value.fillItemGroup(ItemGroup.SEARCH, subItems); + for (ItemStack subItem : subItems) { + tasks.set(tasks.get() + 1); + String subKey = key + (subItem.hasTag() ? "__" + serializeNbtTag(subItem.getTag()) : ""); + //Test if item is BlockItem and if block is opaque + if ((subItem.getItem() instanceof BlockItem) && RenderTypeLookup.canRenderInLayer(((BlockItem) subItem.getItem()).getBlock().getDefaultState(), RenderType.getSolid())) { + exportTasks.add((matrixStack, bImage) -> { + taskProcessed.set(taskProcessed.get() + 1); + signalStatus(tasks, taskProcessed); + fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, NOT_BLACK); + ItemRenderUtil.renderItem(subItem, scaleModified); + NativeImage image = ImageExportUtil.takeScreenshot(this.width, this.height, this.scaleImage); + ImageExportUtil.exportImage(baseDir, subKey, ImageExportUtil.adjustImageAlpha(image, NOT_BLACK_SHIFTED)); + if (subItem.hasTag() && GeneralConfig.fileNameHashTag) { + ImageExportUtil.exportNbtFile(baseDir, subKey, subItem.getTag()); + } + }); + } else { + exportTasks.add((matrixStack, bImage) -> { + fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, BLACK); + ItemRenderUtil.renderItem(subItem, scaleModified); + bImage.set(takeScreenshot(this.width, this.height, this.scaleImage)); + }); + exportTasks.add((matrixStack, bImage) -> { + taskProcessed.set(taskProcessed.get() + 1); + signalStatus(tasks, taskProcessed); + fill(matrixStack, 0, 0, scaleModifiedRounded, scaleModifiedRounded, WHITE); + ItemRenderUtil.renderItem(subItem, scaleModified); + NativeImage wImage = takeScreenshot(this.width, this.height, this.scaleImage); + ImageExportUtil.exportImage(baseDir, subKey, ImageExportUtil.adjustImageAlpha(bImage.get(), wImage)); + if (subItem.hasTag() && GeneralConfig.fileNameHashTag) { + ImageExportUtil.exportNbtFile(baseDir, subKey, subItem.getTag()); + } + }); } - }); + } } } diff --git a/src/main/java/org/cyclops/iconexporter/command/CommandExport.java b/src/main/java/org/cyclops/iconexporter/command/CommandExport.java index 72c73c0..188b948 100644 --- a/src/main/java/org/cyclops/iconexporter/command/CommandExport.java +++ b/src/main/java/org/cyclops/iconexporter/command/CommandExport.java @@ -2,6 +2,7 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -18,22 +19,28 @@ */ public class CommandExport implements Command { - private final boolean param; + private final boolean hasScale; + private final boolean hasNamespace; - public CommandExport(boolean param) { - this.param = param; + public CommandExport(boolean hasScale, boolean hasNamespace) { + this.hasScale = hasScale; + this.hasNamespace = hasNamespace; } @Override public int run(CommandContext context) throws CommandSyntaxException { // Determine the scale int scale = GeneralConfig.defaultScale; - if (param) { + String namespace = null; + if (this.hasScale) { scale = context.getArgument("scale", Integer.class); } + if (this.hasNamespace) { + namespace = context.getArgument("namespace", String.class); + } // Open the gui that will render the icons - ScreenIconExporter exporter = new ScreenIconExporter(scale, Minecraft.getInstance().getMainWindow().getGuiScaleFactor()); + ScreenIconExporter exporter = new ScreenIconExporter(scale, Minecraft.getInstance().getMainWindow().getGuiScaleFactor(), namespace); Minecraft.getInstance().deferTask(() -> Minecraft.getInstance().displayGuiScreen(exporter)); return 0; @@ -41,9 +48,11 @@ public int run(CommandContext context) throws CommandSyntaxExcept public static LiteralArgumentBuilder make() { return Commands.literal("export") - .executes(new CommandExport(false)) + .executes(new CommandExport(false, false)) .then(Commands.argument("scale", IntegerArgumentType.integer(1)) - .executes(new CommandExport(true))); + .then(Commands.argument("namespace", StringArgumentType.word()) + .executes(new CommandExport(true, true))) + .executes(new CommandExport(true, false))); } }