From 9133f05b5e4aec31d0bde831ca78452fc5aa51a6 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Mon, 7 Jan 2019 17:11:43 +0900 Subject: [PATCH] sprite tool exports "formatted" TGA meaning if alpha is zero, RGB is also zero --- build.gradle | 6 ++ src/com/badlogic/gdx/graphics/PixmapIO2.java | 89 +++++++++++++++++++ .../spriteassembler/SpriteAssemblerApp.kt | 36 ++++++-- 3 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 src/com/badlogic/gdx/graphics/PixmapIO2.java diff --git a/build.gradle b/build.gradle index c7d8cbc55..889cc6b8c 100644 --- a/build.gradle +++ b/build.gradle @@ -48,14 +48,20 @@ jar { task game(type: JavaExec) { classpath sourceSets.main.runtimeClasspath main = 'net.torvald.terrarum.AppLoader' + group = "Application" + description = "Launches the game. Should be the same as 'gradlew run'." } task spriteassembler(type: JavaExec) { classpath sourceSets.main.runtimeClasspath main = 'net.torvald.spriteassembler.SpriteAssemblerAppKt' + group = "Application" + description = "Launches the Sprite Assembler." } task csveditor(type: JavaExec) { classpath sourceSets.main.runtimeClasspath main = 'net.torvald.terrarum.debuggerapp.CSVEditor' + group = "Application" + description = "Launches the CSV Editor. (for Blocks?)" } diff --git a/src/com/badlogic/gdx/graphics/PixmapIO2.java b/src/com/badlogic/gdx/graphics/PixmapIO2.java new file mode 100644 index 000000000..b0ebe34f0 --- /dev/null +++ b/src/com/badlogic/gdx/graphics/PixmapIO2.java @@ -0,0 +1,89 @@ +package com.badlogic.gdx.graphics; + +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.StreamUtils; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Created by minjaesong on 2019-01-07. + */ +public class PixmapIO2 { + + public static void writeTGA(FileHandle file, Pixmap pixmap) throws IOException { + OutputStream output = file.write(false); + + try { + _writeTGA(output, pixmap); + } finally { + StreamUtils.closeQuietly(output); + } + } + + private static void _writeTGA(OutputStream out, Pixmap pixmap) throws IOException { + byte[] width = toShortLittle(pixmap.getWidth()); + byte[] height = toShortLittle(pixmap.getHeight()); + byte[] zero = toShortLittle(0); + + byte[] zeroalpha = new byte[]{0,0,0,0}; + + out.write(0); // ID field: empty + out.write(0); // no colour map, but should be ignored anyway as it being unmapped RGB + out.write(2); // 2 means unmapped RGB + out.write(new byte[]{0,0,0,0,0}); // color map spec: empty + out.write(zero); // x origin: 0 + out.write(zero); // y origin: 0 + out.write(width); // width + out.write(height); // height + out.write(32); // image pixel size: we're writing 32-bit image (8bpp BGRA) + out.write(8); // image descriptor: dunno, Photoshop writes 8 in there + + // write actual image data + // since we're following Photoshop's conventional header, we also follows Photoshop's + // TGA saving scheme, that is: + // 1. BGRA order + // 2. Y-Flipped but not X-Flipped + + for (int y = pixmap.getHeight() - 1; y >= 0; y--) { + for (int x = 0; x < pixmap.getWidth(); x++) { + int color = pixmap.getPixel(x, y); + + // if alpha == 0, write special value instead + if ((color & 0xFF) == 0) { + out.write(zeroalpha); + } + else { + out.write(RGBAtoBGRA(color)); + } + } + } + + + // write footer + // 00 00 00 00 00 00 00 00 TRUEVISION-XFILE 2E 00 + out.write(new byte[]{0,0,0,0,0,0,0,0}); + out.write("TerrarumHappyTGA".getBytes()); + out.write(new byte[]{0x2E,0}); + + + out.flush(); + out.close(); + } + + private static byte[] toShortLittle(int i) { + return new byte[]{ + (byte) (i & 0xFF), + (byte) ((i >>> 8) & 0xFF) + }; + } + + private static byte[] RGBAtoBGRA(int rgba) { + return new byte[]{ + (byte) ((rgba >>> 8) & 0xFF), + (byte) ((rgba >>> 16) & 0xFF), + (byte) ((rgba >>> 24) & 0xFF), + (byte) (rgba & 0xFF) + }; + } +} diff --git a/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt b/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt index e3fc711a7..bd27efd04 100644 --- a/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt +++ b/src/net/torvald/spriteassembler/SpriteAssemblerApp.kt @@ -4,10 +4,7 @@ import com.badlogic.gdx.Game import com.badlogic.gdx.Gdx import com.badlogic.gdx.backends.lwjgl.LwjglApplication import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration -import com.badlogic.gdx.graphics.Color -import com.badlogic.gdx.graphics.GL20 -import com.badlogic.gdx.graphics.Pixmap -import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch import net.torvald.terrarum.inUse import java.awt.BorderLayout @@ -76,8 +73,7 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() { "ADD_ROWS=Enter the number of rows to add:\n" + "WRITE_FAIL=Writing to file has failed:\n" + "STAT_INIT=Creating a new CSV. You can still open existing file.\n" + - "STAT_SAVE_SUCCESSFUL=File saved successfully.\n" + - "STAT_NEW_FILE=New CSV created.\n" + + "STAT_SAVE_TGA_SUCCESSFUL=Spritesheet exported successfully.\n" + "STAT_LOAD_SUCCESSFUL=File loaded successfully.\n" + "ERROR_INTERNAL=Something went wrong.\n" + "ERROR_PARSE_FAIL=Parsing failed\n" + @@ -122,7 +118,6 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() { panelMain.resizeWeight = 0.666 val menu = JMenuBar() - menu.add(JMenu("File")) menu.add(JMenu("Parse")).addMouseListener(object : MouseAdapter() { override fun mousePressed(e: MouseEvent?) { try { @@ -193,6 +188,17 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() { } } }) + menu.add(JMenu("Export")).addMouseListener(object : MouseAdapter() { + override fun mousePressed(e: MouseEvent?) { + val fileChooser = JFileChooser() + fileChooser.showSaveDialog(null) + + if (fileChooser.selectedFile != null) { + gdxWindow.requestExport(fileChooser.selectedFile.absolutePath) + statBar.text = lang.getProperty("STAT_SAVE_TGA_SUCCESSFUL") + } // else, do nothing + } + }) this.layout = BorderLayout() this.add(menu, BorderLayout.NORTH) @@ -250,6 +256,9 @@ class SpriteAssemblerPreview: Game() { private var doAssemble = false private lateinit var assembleProp: ADProperties + private var doExport = false + private lateinit var exportPath: String + override fun render() { if (doAssemble) { // assembly requires GL context @@ -257,6 +266,11 @@ class SpriteAssemblerPreview: Game() { assembleImage(assembleProp) } + if (doExport && image != null) { + doExport = false + PixmapIO2.writeTGA(Gdx.files.absolute(exportPath), image) + } + Gdx.gl.glClearColor(.62f,.79f,1f,1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) @@ -280,6 +294,14 @@ class SpriteAssemblerPreview: Game() { assembleProp = prop } + fun requestExport(path: String) { + doExport = true + exportPath = path + } + + override fun resize(width: Int, height: Int) { + super.resize(width, height) + } } fun main(args: Array) {