diff --git a/README-Eng.md b/README-Eng.md index e9fc240..3d9ed1c 100644 --- a/README-Eng.md +++ b/README-Eng.md @@ -4,7 +4,7 @@ A tool to load and export Spine animations **Java 21** required Pixel Buffers support required Exporting MOV requires FFmpeg -Current version: 2.0.5 +Current version: 2.1.0 ![霜叶](https://i0.hdslb.com/bfs/album/98b4fd8a12bc6dbf691b967bed625db67713dff0.png@518w.png "明日方舟 - 霜叶") @@ -12,6 +12,10 @@ Current version: 2.0.5 [**Released Stable Version**](https://github.com/Aloento/SuperSpineViewer/releases/latest) +```bash +java -XX:MaxRAMPercentage=75.0 --enable-preview -jar SuperSpineViewer.jar +``` + ### Performance Settings Reference * High Resolution (Camera) = High Memory Requirements diff --git a/README.md b/README.md index a18c3de..ded6201 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 需要 **Java 21 21 21** 需要 像素缓冲区支持 导出 MOV 需要 FFmpeg -当前版本:2.0.5 +当前版本:2.1.0 ![霜叶](https://i0.hdslb.com/bfs/album/98b4fd8a12bc6dbf691b967bed625db67713dff0.png@518w.png "明日方舟 - 霜叶") @@ -14,6 +14,10 @@ [**发布的稳定版本**](https://github.com/Aloento/SuperSpineViewer/releases/latest) +```bash +java -XX:MaxRAMPercentage=75.0 --enable-preview -jar SuperSpineViewer.jar +``` + ### 性能设置参考 * 高分辨率 (Camera) = 高内存需求 diff --git a/pom.xml b/pom.xml index 768701d..6b01db4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ to.aloen SuperSpineViewer - 2.0.5 + 2.1.0 sonatype @@ -88,6 +88,11 @@ javafx-fxml 21.0.2 + + org.openjfx + javafx-swing + 21.0.2 + org.rationalityfrontline.workaround jfoenix diff --git a/src/main/java/to/aloen/ssv/Loader.java b/src/main/java/to/aloen/ssv/Loader.java index 1909ff7..b002f2c 100644 --- a/src/main/java/to/aloen/ssv/Loader.java +++ b/src/main/java/to/aloen/ssv/Loader.java @@ -131,12 +131,14 @@ public static void init() { gdxApp = new LwjglFXApplication(adapter, Main.spineRender); } } else { - new Thread(() -> { + Thread load = new Thread(() -> { if (Main.spineController.isLoaded()) { gdxApp = new LwjglFXApplication(adapter, Main.spineRender); Main.spineController = null; } - }, "Loading").start(); + }, "Loading"); + load.setDaemon(true); + load.start(); } } } diff --git a/src/main/java/to/aloen/ssv/RecordFX.java b/src/main/java/to/aloen/ssv/RecordFX.java index a5889ca..81afdfa 100644 --- a/src/main/java/to/aloen/ssv/RecordFX.java +++ b/src/main/java/to/aloen/ssv/RecordFX.java @@ -1,30 +1,34 @@ package to.aloen.ssv; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.PixmapIO; import javafx.application.Platform; +import javafx.embed.swing.SwingFXUtils; import javafx.scene.image.PixelReader; +import javafx.scene.image.PixelWriter; +import javafx.scene.image.WritableImage; +import javafx.scene.paint.Color; import to.aloen.spine.Spine; +import javax.imageio.ImageIO; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import java.util.zip.Deflater; public abstract class RecordFX { private static final LinkedBlockingQueue savePool = new LinkedBlockingQueue<>() {{ - new Thread(() -> { + Thread saving = new Thread(() -> { while (true) { try { take().run(); } catch (InterruptedException ignored) { } } - }).start(); + }, "Long Saving"); + saving.setDaemon(true); + saving.start(); }}; private static String fileName; @@ -49,7 +53,7 @@ public static void Start(String fileName) { Thread.onSpinWait(); RecordFX.fileName = fileName; - Main.recording = true; + Platform.runLater(() -> Main.recording = true); } } @@ -127,28 +131,30 @@ private static void encodeFX() { }); } - private static Pixmap toPixmap(final PixelReader image) { - final Pixmap pixmap = new Pixmap(Main.width, Main.height, Pixmap.Format.RGBA8888); + private static WritableImage flipY(final PixelReader image) { + final WritableImage flippedImage = new WritableImage(Main.width, Main.height); + final PixelWriter pixelWriter = flippedImage.getPixelWriter(); - for (int h = 0; h < Main.height; h++) { - for (int w = 0; w < Main.width; w++) { - int argb = image.getArgb(w, h); - pixmap.drawPixel(w, h, (argb << 8) | (argb >>> 24)); + for (int y = 0; y < Main.height; y++) { + for (int x = 0; x < Main.width; x++) { + Color color = image.getColor(x, y); + pixelWriter.setColor(x, Main.height - 1 - y, color); } } - return pixmap; + return flippedImage; } - private static void writePNG(final Pixmap pixmap, final short index) { + private static void writePNG(final WritableImage image, final short index) { try { - PixmapIO.writePNG(Gdx.files.absolute( - STR."\{Main.outPath}\{fileName}_Sequence\{File.separator}\{fileName}_\{index}.png"), - pixmap, Deflater.NO_COMPRESSION, true - ); - } finally { - pixmap.dispose(); + File outputFile = new File(STR."\{Main.outPath}\{fileName}_Sequence\{File.separator}\{fileName}_\{index}.png"); + outputFile.getParentFile().mkdirs(); + outputFile.createNewFile(); + ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", outputFile); + } catch (IOException e) { + e.printStackTrace(); + } finally { if (!Main.recording) { var percent = (double) items++ / counter; Platform.runLater(() -> Main.progressBar.setProgress(percent)); @@ -170,9 +176,9 @@ private Task(PixelReader image, short counter) { @Override public void run() { - final Pixmap pixmap = toPixmap(image); + final WritableImage flipY = flipY(image); image = null; - writePNG(pixmap, counter); + writePNG(flipY, counter); } } } diff --git a/src/main/java/to/aloen/ssv/controller/ExporterController.java b/src/main/java/to/aloen/ssv/controller/ExporterController.java index d3f94bd..a463bd7 100644 --- a/src/main/java/to/aloen/ssv/controller/ExporterController.java +++ b/src/main/java/to/aloen/ssv/controller/ExporterController.java @@ -58,8 +58,10 @@ void B_Export() { Spine.speed.set(Main.quality); Spine.isPlay.set(true); - System.out.println("请求:开始录制"); - RecordFX.Start(STR."\{Spine.projectName.get()}_\{Spine.animate.get()}"); + Thread.startVirtualThread(() -> { + RecordFX.Start(STR."\{Spine.projectName.get()}_\{Spine.animate.get()}"); + System.out.println("请求:开始录制"); + }); } } diff --git a/src/main/java/to/aloen/ssv/controller/SpineController.java b/src/main/java/to/aloen/ssv/controller/SpineController.java index d6087c4..99d67f9 100644 --- a/src/main/java/to/aloen/ssv/controller/SpineController.java +++ b/src/main/java/to/aloen/ssv/controller/SpineController.java @@ -317,13 +317,13 @@ public void initialize(URL location, ResourceBundle resources) { fitWidthProperty().addListener((_, _, newValue) -> { T_Width.setPromptText(String.valueOf(newValue.intValue())); width = newValue.intValue(); - Pref.putDouble("stageWidth", newValue.doubleValue() + 368); + Pref.putDouble("stageWidth", newValue.doubleValue() + 350); }); fitHeightProperty().addListener((_, _, newValue) -> { T_Height.setPromptText(String.valueOf(newValue.intValue())); height = newValue.intValue(); - Pref.putDouble("stageHeight", newValue.doubleValue() + 103); + Pref.putDouble("stageHeight", newValue.doubleValue() + 50); }); }}; @@ -367,8 +367,8 @@ public boolean isLoaded() { Viewer.getChildren().remove(loadPane); Viewer.setCenter(spineRender); - spineRender.fitHeightProperty().bind(spineRender.getScene().heightProperty().add(-103)); - spineRender.fitWidthProperty().bind(spineRender.getScene().widthProperty().add(-368)); + spineRender.fitHeightProperty().bind(spineRender.getScene().heightProperty().add(-50)); + spineRender.fitWidthProperty().bind(spineRender.getScene().widthProperty().add(-350)); Viewer = null; loadPane = null;