From f01b8bc126f6c83f1845a6c61a9693715e6a260d Mon Sep 17 00:00:00 2001 From: lax1dude Date: Mon, 17 Jun 2024 21:46:37 -0700 Subject: [PATCH] Implement Eagler BMP generator --- .../eaglercraft/bintools/EaglerBMPGen.java | 101 ++++++++++++++++++ .../bintools/EaglerBinaryTools.java | 11 +- 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBMPGen.java diff --git a/src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBMPGen.java b/src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBMPGen.java new file mode 100644 index 0000000..c5480d6 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBMPGen.java @@ -0,0 +1,101 @@ +package net.lax1dude.eaglercraft.bintools; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import javax.imageio.ImageIO; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerBMPGen { + + public static void _main(String[] args) throws IOException { + if(args.length != 3 || (!args[0].equalsIgnoreCase("--red") && !args[0].equalsIgnoreCase("--rgba"))) { + System.out.println("Usage: eagler-bmp-gen <--red|--rgba> "); + System.out.println("The texture's width and height must be a power of 2!"); + return; + } + System.out.println("Reading input file..."); + BufferedImage img = ImageIO.read(new File(args[1])); + File output = new File(args[2]); + System.out.println("Encoding Eagler BMP: " + output.getAbsolutePath()); + int lvls; + try(FileOutputStream fs = new FileOutputStream(output)) { + ByteArrayOutputStream bao = new ByteArrayOutputStream(0x7FFF); + lvls = encode(img, args[0].equalsIgnoreCase("--rgba"), bao); + fs.write(bao.toByteArray()); + } + System.out.println("Wrote " + lvls + " mipmap levels"); + } + + public static int encode(BufferedImage input, boolean rgba, OutputStream os) throws IOException { + boolean invalid = false; + if(!isPowOf2(input.getWidth())) { + System.err.println("The image's width is not a power of 2!"); + invalid = true; + } + if(!isPowOf2(input.getHeight())) { + System.err.println("The image's height is not a power of 2!"); + invalid = true; + } + if(invalid) { + throw new IOException("The image has invalid dimensions!"); + } + DataOutputStream dos = new DataOutputStream(os); + int lvls = 0; + while(true) { + ++lvls; + dos.write('E'); + dos.writeShort(input.getWidth()); + dos.writeShort(input.getHeight()); + for(int y = 0; y < input.getHeight(); ++y) { + for(int x = 0; x < input.getWidth(); ++x) { + int c = input.getRGB(x, input.getHeight() - y - 1); + dos.write((c >>> 16) & 0xFF); + if(rgba) { + dos.write((c >>> 8) & 0xFF); + dos.write((c >>> 0) & 0xFF); + dos.write((c >>> 24) & 0xFF); + } + } + } + if(input.getWidth() > 1 && input.getHeight() > 1) { + BufferedImage img2 = new BufferedImage(input.getWidth() >> 1, input.getHeight() >> 1, BufferedImage.TYPE_INT_ARGB); + Graphics g = img2.getGraphics(); + ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(input, 0, 0, img2.getWidth(), img2.getHeight(), 0, 0, input.getWidth(), input.getHeight(), null); + g.dispose(); + input = img2; + }else { + dos.write(0); + break; + } + } + return lvls; + } + + private static boolean isPowOf2(int x) { + return (x & (x - 1)) == 0; + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBinaryTools.java b/src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBinaryTools.java index e1e3baa..bf80733 100644 --- a/src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBinaryTools.java +++ b/src/main/java/net/lax1dude/eaglercraft/bintools/EaglerBinaryTools.java @@ -67,11 +67,9 @@ public class EaglerBinaryTools { case "lightmeshgen": LightMeshGen._main(argz); return; - case "eagler-moon-gen": - case "eaglermoongen": - return; - case "lens-flare-gen": - case "lensflaregen": + case "eagler-bmp-gen": + case "eaglerbmpgen": + EaglerBMPGen._main(argz); return; default: usage(); @@ -90,8 +88,7 @@ public class EaglerBinaryTools { System.out.println(" - 'ebp-decode': Decode EBP file to PNG"); System.out.println(" - 'skybox-gen': Generate skybox.dat from OBJ for shader packs"); System.out.println(" - 'light-mesh-gen': Generate light_point.dat from OBJ for shader packs"); - System.out.println(" - 'eagler-moon-gen': Generate eagler_moon.bmp from PNG for shader packs"); - System.out.println(" - 'lens-flare-gen': Generate lens_streaks.bmp, lens_ghosts.bmp from PNG for shader packs"); + System.out.println(" - 'eagler-bmp-gen': Generate moon and lens flare BMP textures from PNG for shader packs"); } }