From 6f615ac571c238644def37c1a11210898942a6ac Mon Sep 17 00:00:00 2001 From: oneachina Date: Fri, 2 Jan 2026 18:59:05 +0800 Subject: [PATCH 01/21] chore: Remove .idea and .gradle directories from version control --- .gitignore | 1 + .idea/.gitignore | 3 - .idea/MarsCodeWorkspaceAppSettings.xml | 8 -- .idea/compiler.xml | 6 - .idea/encodings.xml | 78 ----------- .idea/gradle.xml | 17 --- .idea/inspectionProfiles/Project_Default.xml | 15 --- .idea/jarRepositories.xml | 75 ----------- .idea/migrateChatHistory.xml | 10 -- .idea/misc.xml | 16 --- .idea/modules.xml | 12 -- .idea/modules/SoarClient-CN.iml | 9 -- .idea/modules/SoarClient-CN.main.iml | 22 ---- .idea/modules/SoarClient-CN.test.iml | 18 --- .../cn.pupperclient.SoarClient-CN.main.iml | 18 --- .../cn.pupperclient.SoarClient-CN.test.iml | 18 --- .idea/runConfigurations/Minecraft_Client.xml | 12 -- .idea/runConfigurations/Minecraft_Server.xml | 16 --- .idea/uiDesigner.xml | 124 ------------------ .idea/vcs.xml | 6 - 20 files changed, 1 insertion(+), 483 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/MarsCodeWorkspaceAppSettings.xml delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/migrateChatHistory.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/modules/SoarClient-CN.iml delete mode 100644 .idea/modules/SoarClient-CN.main.iml delete mode 100644 .idea/modules/SoarClient-CN.test.iml delete mode 100644 .idea/modules/cn.pupperclient.SoarClient-CN.main.iml delete mode 100644 .idea/modules/cn.pupperclient.SoarClient-CN.test.iml delete mode 100644 .idea/runConfigurations/Minecraft_Client.xml delete mode 100644 .idea/runConfigurations/Minecraft_Server.xml delete mode 100644 .idea/uiDesigner.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index ca7acaf..60fbfa6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ bin run .skidfuscator +.idea diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 359bb53..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml diff --git a/.idea/MarsCodeWorkspaceAppSettings.xml b/.idea/MarsCodeWorkspaceAppSettings.xml deleted file mode 100644 index d4a52f0..0000000 --- a/.idea/MarsCodeWorkspaceAppSettings.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b86273d..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 8b8d44e..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 7d3b3e8..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index e754f00..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 4cc5492..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/migrateChatHistory.xml b/.idea/migrateChatHistory.xml deleted file mode 100644 index 94b2914..0000000 --- a/.idea/migrateChatHistory.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 61b50dc..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index a02bf5b..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/SoarClient-CN.iml b/.idea/modules/SoarClient-CN.iml deleted file mode 100644 index 2c92d42..0000000 --- a/.idea/modules/SoarClient-CN.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/SoarClient-CN.main.iml b/.idea/modules/SoarClient-CN.main.iml deleted file mode 100644 index 750b879..0000000 --- a/.idea/modules/SoarClient-CN.main.iml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - FABRIC - MIXIN - MCP - - 1 - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/SoarClient-CN.test.iml b/.idea/modules/SoarClient-CN.test.iml deleted file mode 100644 index 0fee84f..0000000 --- a/.idea/modules/SoarClient-CN.test.iml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - FABRIC - MIXIN - MCP - - 1 - - - - - - \ No newline at end of file diff --git a/.idea/modules/cn.pupperclient.SoarClient-CN.main.iml b/.idea/modules/cn.pupperclient.SoarClient-CN.main.iml deleted file mode 100644 index 0fee84f..0000000 --- a/.idea/modules/cn.pupperclient.SoarClient-CN.main.iml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - FABRIC - MIXIN - MCP - - 1 - - - - - - \ No newline at end of file diff --git a/.idea/modules/cn.pupperclient.SoarClient-CN.test.iml b/.idea/modules/cn.pupperclient.SoarClient-CN.test.iml deleted file mode 100644 index 0fee84f..0000000 --- a/.idea/modules/cn.pupperclient.SoarClient-CN.test.iml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - FABRIC - MIXIN - MCP - - 1 - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/Minecraft_Client.xml b/.idea/runConfigurations/Minecraft_Client.xml deleted file mode 100644 index d5eb21a..0000000 --- a/.idea/runConfigurations/Minecraft_Client.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/Minecraft_Server.xml b/.idea/runConfigurations/Minecraft_Server.xml deleted file mode 100644 index c3a8521..0000000 --- a/.idea/runConfigurations/Minecraft_Server.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml deleted file mode 100644 index 2b63946..0000000 --- a/.idea/uiDesigner.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 20587bef2679c0c2e9bdbd94e85fa28559ca5e88 Mon Sep 17 00:00:00 2001 From: oneachina Date: Fri, 2 Jan 2026 18:59:47 +0800 Subject: [PATCH 02/21] feat: Prepare for Minecraft 1.21.5 update --- build.gradle | 32 +++++++++++++++--------------- gradle.properties | 11 +++++----- src/main/resources/fabric.mod.json | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/build.gradle b/build.gradle index 355267f..bdd9fdc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.14-SNAPSHOT' + id 'fabric-loom' version "1.14-SNAPSHOT" } project.ext.lwjglVersion = "3.3.3" @@ -35,18 +35,18 @@ dependencies { minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - - modImplementation "com.viaversion:viafabricplus-api:4.0.2" - modRuntimeOnly "maven.modrinth:sodium:mc1.21.4-0.6.6-fabric" - modRuntimeOnly "maven.modrinth:iris:1.8.5+1.21.4-fabric" - modRuntimeOnly "maven.modrinth:sodium-extra:mc1.21.4-0.6.1+fabric" - modRuntimeOnly "maven.modrinth:lithium:mc1.21.4-0.14.7-fabric" - modImplementation "maven.modrinth:in-game-account-switcher:WBbjirJP" - modImplementation "com.viaversion:viafabricplus:4.0.2" - modRuntimeOnly "maven.modrinth:immediatelyfast:1.3.3+1.21.4-fabric" - modRuntimeOnly "maven.modrinth:entityculling:NkBXk0Ye" - modImplementation "maven.modrinth:modmenu:13.0.3" + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" + + modImplementation "com.viaversion:viafabricplus-api:4.4.1" + modRuntimeOnly "maven.modrinth:sodium:mc1.21.5-0.6.13-fabric" + modRuntimeOnly "maven.modrinth:iris:1.8.11+1.21.5-fabric" + modRuntimeOnly "maven.modrinth:sodium-extra:mc1.21.5-0.6.3+fabric" + modRuntimeOnly "maven.modrinth:lithium:mc1.21.5-0.16.3-fabric" + modImplementation "maven.modrinth:in-game-account-switcher:Rqmwlwr6" + modImplementation "com.viaversion:viafabricplus:4.4.1" + modRuntimeOnly "maven.modrinth:immediatelyfast:1.9.7+1.21.5-fabric" + modRuntimeOnly "maven.modrinth:entityculling:ldvBBWG2" + modImplementation "maven.modrinth:modmenu:14.0.0" jij 'io.github.smartboot.socket:aio-core:1.7.1' jij 'com.github.ben-manes.caffeine:caffeine:3.1.8' @@ -64,10 +64,10 @@ dependencies { jij 'com.googlecode.soundlibs:jlayer:1.0.1.4' jij "net.lenni0451:MCPing:1.4.2" jij "net.lenni0451:Reflect:1.4.0" - modImplementation 'net.java.dev.jna:jna:5.12.1' - modImplementation 'net.java.dev.jna:jna-platform:5.12.1' + modImplementation 'net.java.dev.jna:jna:5.18.1' + modImplementation 'net.java.dev.jna:jna-platform:5.18.1' - modJij 'com.github.CCBlueX:mcef:3.0.0-1.21.4' + modJij 'com.github.CCBlueX:mcef:3.1.3-1.21.5' def lwjglNfdDeps = [ "org.lwjgl:lwjgl-nfd:$lwjglVersion", diff --git a/gradle.properties b/gradle.properties index a842401..c749112 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,14 +4,15 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.4 -yarn_mappings=1.21.4+build.8 -loader_version=0.18.2 +minecraft_version=1.21.5 +yarn_mappings=1.21.5+build.1 +loader_version=0.18.4 + +# Fabric API +fabric_api_version=0.128.2+1.21.5 # Mod Properties mod_version=25.12.3 maven_group=cn.pupperclient archives_base_name=pupper -# Dependencies -fabric_version=0.119.4+1.21.4 diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index fce97ca..24e992c 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -21,7 +21,7 @@ "depends": { "fabricloader": ">=0.16.10", "fabric": "*", - "minecraft": "1.21.4", + "minecraft": "1.21.5", "viafabricplus": "*", "ias": "*" }, From faf430c812c7ef8d107f69414d0e349c5f3b39a8 Mon Sep 17 00:00:00 2001 From: oneachina Date: Fri, 2 Jan 2026 19:00:22 +0800 Subject: [PATCH 03/21] chore: Bump Mod Version --- gradle.properties | 2 +- src/main/java/cn/pupperclient/PupperClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index c749112..28c9d88 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ loader_version=0.18.4 fabric_api_version=0.128.2+1.21.5 # Mod Properties -mod_version=25.12.3 +mod_version=26.1.1 maven_group=cn.pupperclient archives_base_name=pupper diff --git a/src/main/java/cn/pupperclient/PupperClient.java b/src/main/java/cn/pupperclient/PupperClient.java index 36f9ef7..c8183f9 100644 --- a/src/main/java/cn/pupperclient/PupperClient.java +++ b/src/main/java/cn/pupperclient/PupperClient.java @@ -37,7 +37,7 @@ public class PupperClient implements IMinecraft { private static final String CONFIG_FILE_NAME = "pupper.ok"; private static final String ICON_PATH = "assets/pupper/logo.png"; private static final String CLIENT_NAME = "Pupper Client"; - private static final String CLIENT_VERSION = "25.12.3"; + private static final String CLIENT_VERSION = "26.1.1"; private static final PupperClient INSTANCE = new PupperClient(); From 18e5046c840cc4a210f37c1fb5fa148c8ebe2164 Mon Sep 17 00:00:00 2001 From: oneachina Date: Fri, 2 Jan 2026 19:31:37 +0800 Subject: [PATCH 04/21] chore: rename prefix --- src/main/java/cn/pupperclient/PupperLogger.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/cn/pupperclient/PupperLogger.java b/src/main/java/cn/pupperclient/PupperLogger.java index 05cd4e5..8d0479e 100644 --- a/src/main/java/cn/pupperclient/PupperLogger.java +++ b/src/main/java/cn/pupperclient/PupperLogger.java @@ -8,19 +8,19 @@ public class PupperLogger { private static final Logger logger = LogManager.getLogger("PupperClient Client"); public static void info(String prefix, String message) { - logger.info("[SC/INFO] [" + prefix + "] " + message); + logger.info("[PupperClient/INFO] [{}] {}", prefix, message); } public static void warn(String prefix, String message) { - logger.warn("[SC/WARN] [" + prefix + "] " + message); + logger.warn("[PupperClient/WARN] [{}] {}", prefix, message); } public static void error(String prefix, String message) { - logger.error("[SC/ERROR] [" + prefix + "] " + message); + logger.error("[PupperClient/ERROR] [{}] {}", prefix, message); } public static void error(String prefix, String message, Exception e) { - logger.error("[SC/ERROR] [" + prefix + "] " + message, e); + logger.error("[PupperClient/ERROR] [{}] {}", prefix, message, e); } public static Logger getLogger() { From ed6e54629847a59f5112af509ff717bbc49fcace Mon Sep 17 00:00:00 2001 From: oneachina Date: Fri, 2 Jan 2026 22:58:42 +0800 Subject: [PATCH 05/21] fix: Remove duplicate API folder within the API directory --- .../gui/api/page/impl/api/SimpleSoarGui.java | 113 -------- .../gui/api/page/impl/api/SoarGui.java | 264 ------------------ .../api/page/impl/api/page/GuiTransition.java | 18 -- .../gui/api/page/impl/api/page/Page.java | 71 ----- .../api/page/impl/api/page/SimplePage.java | 110 -------- .../api/page/impl/LeftRightTransition.java | 26 -- .../impl/api/page/impl/LeftTransition.java | 26 -- .../api/page/impl/RightLeftTransition.java | 26 -- .../impl/api/page/impl/RightTransition.java | 26 -- 9 files changed, 680 deletions(-) delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/SimpleSoarGui.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/SoarGui.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/page/GuiTransition.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/page/Page.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/page/SimplePage.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftRightTransition.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftTransition.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightLeftTransition.java delete mode 100644 src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightTransition.java diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/SimpleSoarGui.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/SimpleSoarGui.java deleted file mode 100644 index 92ecb06..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/SimpleSoarGui.java +++ /dev/null @@ -1,113 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api; - -import cn.pupperclient.skia.Skia; -import cn.pupperclient.skia.context.SkiaContext; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.Text; - -public class SimpleSoarGui { - public MinecraftClient client = MinecraftClient.getInstance(); - private final boolean mcScale; - - public SimpleSoarGui(boolean mcScale) { - this.mcScale = mcScale; - } - - public void init() { - } - - public void draw(double mouseX, double mouseY) { - } - - public void mousePressed(double mouseX, double mouseY, int button) { - } - - public void mouseReleased(double mouseX, double mouseY, int button) { - } - - public void mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { - } - - public void charTyped(char chr, int modifiers) { - } - - public void keyPressed(int keyCode, int scanCode, int modifiers) { - } - - public Screen build() { - return new Screen(Text.empty()) { - - @Override - public void init() { - SimpleSoarGui.this.init(); - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - - SkiaContext.draw((skiaContext) -> { - - Skia.save(); - - if (mcScale) { - if (client != null) { - Skia.scale((float) client.getWindow().getScaleFactor()); - } - } - - if (client != null) { - SimpleSoarGui.this.draw(mcScale ? mouseX : client.mouse.getX(), - mcScale ? mouseY : client.mouse.getY()); - } - Skia.restore(); - }); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (client != null) { - SimpleSoarGui.this.mousePressed(mcScale ? mouseX : client.mouse.getX(), - mcScale ? mouseY : client.mouse.getY(), button); - } - return true; - } - - @Override - public boolean mouseReleased(double mouseX, double mouseY, int button) { - if (client != null) { - SimpleSoarGui.this.mouseReleased(mcScale ? mouseX : client.mouse.getX(), - mcScale ? mouseY : (int) client.mouse.getY(), button); - } - return true; - } - - @Override - public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { - if (client != null) { - SimpleSoarGui.this.mouseScrolled(mcScale ? mouseX : client.mouse.getX(), - mcScale ? mouseY : (int) client.mouse.getY(), horizontalAmount, verticalAmount); - } - return true; - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - SimpleSoarGui.this.keyPressed(keyCode, scanCode, modifiers); - return true; - } - - @Override - public boolean charTyped(char chr, int modifiers) { - SimpleSoarGui.this.charTyped(chr, modifiers); - return true; - } - - @Override - public boolean shouldPause() { - return false; - } - }; - } -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/SoarGui.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/SoarGui.java deleted file mode 100644 index 9781682..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/SoarGui.java +++ /dev/null @@ -1,264 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api; - -import cn.pupperclient.PupperClient; -import cn.pupperclient.animation.Animation; -import cn.pupperclient.animation.Duration; -import cn.pupperclient.animation.cubicbezier.impl.EaseEmphasizedDecelerate; -import cn.pupperclient.gui.api.page.impl.api.page.GuiTransition; -import cn.pupperclient.gui.api.page.impl.api.page.SimplePage; -import cn.pupperclient.management.color.api.ColorPalette; -import cn.pupperclient.management.config.ConfigType; -import cn.pupperclient.management.mod.impl.settings.ModMenuSettings; -import cn.pupperclient.shader.impl.Kawaseblur; -import cn.pupperclient.skia.Skia; -import cn.pupperclient.ui.component.Component; -import cn.pupperclient.utils.Multithreading; -import io.github.humbleui.skija.SurfaceOrigin; -import net.minecraft.client.gui.screen.Screen; -import org.lwjgl.glfw.GLFW; - -import java.util.ArrayList; -import java.util.List; - -public abstract class SoarGui extends SimpleSoarGui { - - protected List components = new ArrayList<>(); - protected List pages; - - protected SimplePage currentPage; - protected SimplePage lastPage; - - private Animation inOutAnimation; - private boolean closable; - private Screen nextScreen; - - public SoarGui(boolean mcScale) { - super(mcScale); - - this.pages = createPages(); - - if (!pages.isEmpty()) { - this.currentPage = pages.getFirst(); - } - } - - @Override - public void init() { - setPageSize(currentPage); - inOutAnimation = new EaseEmphasizedDecelerate(Duration.EXTRA_LONG_1, 0, 1); - closable = true; - currentPage.init(); - } - - public void setPageSize(SimplePage p) { - p.setX(getX()); - p.setY(getY()); - p.setWidth(getWidth()); - p.setHeight(getHeight()); - } - - @Override - public void draw(double mouseX, double mouseY) { - - ColorPalette palette = PupperClient.getInstance().getColorManager().getPalette(); - - if (ModMenuSettings.getInstance().getBlurSetting().isEnabled()) { - Skia.drawImage(Kawaseblur.GUI_BLUR.getTexture(), 0, 0, client.getWindow().getWidth(), - client.getWindow().getHeight(), inOutAnimation.getValue(), SurfaceOrigin.BOTTOM_LEFT); - } - - Skia.save(); - Skia.setAlpha((int) (inOutAnimation.getValue() * 255)); - Skia.scale(getX(), getY(), getWidth(), getHeight(), 2 - inOutAnimation.getValue()); - - Skia.clip(getX(), getY(), getWidth(), getHeight(), 35); - Skia.drawRoundedRect(getX(), getY(), getWidth(), getHeight(), 35, palette.getSurfaceContainer()); - - if (currentPage != null && lastPage == null) { - currentPage.draw(mouseX, mouseY); - } - - if (lastPage != null) { - - GuiTransition transition = lastPage.getTransition(); - - if (currentPage.getTransition().isConsecutive()) { - - Skia.save(); - - if (transition != null) { - float[] result = transition.onTransition(lastPage.getAnimation()); - Skia.translate(result[0] * getWidth(), result[1] * getHeight()); - } - - lastPage.draw(mouseX, mouseY); - Skia.restore(); - } - - Skia.save(); - transition = currentPage.getTransition(); - - if (transition != null) { - float[] result = transition.onTransition(currentPage.getAnimation()); - Skia.translate(result[0] * getWidth(), result[1] * getHeight()); - } - - currentPage.draw(mouseX, mouseY); - Skia.restore(); - - if (lastPage.getAnimation().isFinished()) { - lastPage = null; - } - } - - for (Component c : components) { - c.draw(mouseX, mouseY); - } - - Skia.restore(); - - if (inOutAnimation.getEnd() == 0 && inOutAnimation.isFinished()) { - client.setScreen(nextScreen); - nextScreen = null; - } - } - - @Override - public void mousePressed(double mouseX, double mouseY, int button) { - - if (currentPage != null) { - currentPage.mousePressed(mouseX, mouseY, button); - } - - for (Component c : components) { - c.mousePressed(mouseX, mouseY, button); - } - } - - @Override - public void mouseReleased(double mouseX, double mouseY, int button) { - - if (currentPage != null) { - currentPage.mouseReleased(mouseX, mouseY, button); - } - - for (Component c : components) { - c.mouseReleased(mouseX, mouseY, button); - } - } - - @Override - public void mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { - if (currentPage != null) { - currentPage.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); - } - } - - @Override - public void charTyped(char chr, int modifiers) { - - if (currentPage != null) { - currentPage.charTyped(chr, modifiers); - } - - for (Component c : components) { - c.charTyped(chr, modifiers); - } - } - - @Override - public void keyPressed(int keyCode, int scanCode, int modifiers) { - - if (keyCode == GLFW.GLFW_KEY_ESCAPE && inOutAnimation.getEnd() == 1 && closable) { - close(); - } - - if (currentPage != null) { - currentPage.keyPressed(keyCode, scanCode, modifiers); - } - - for (Component c : components) { - c.keyPressed(keyCode, scanCode, modifiers); - } - } - - public void close(Screen nextScreen) { - if (inOutAnimation.getEnd() == 1) { - this.nextScreen = nextScreen; - inOutAnimation = new EaseEmphasizedDecelerate(Duration.EXTRA_LONG_1, 1, 0); - Multithreading.runAsync(() -> { - PupperClient.getInstance().getConfigManager().save(ConfigType.MOD); - }); - } - } - - public void close() { - close(null); - } - - public SimplePage getCurrentPage() { - return currentPage; - } - - public void setCurrentPage(SimplePage page) { - - if (currentPage != null) { - lastPage = currentPage; - currentPage.onClosed(); - } - - this.currentPage = page; - currentPage.setAnimation(new EaseEmphasizedDecelerate(Duration.MEDIUM_1, 0, 1)); - lastPage.setAnimation(new EaseEmphasizedDecelerate(Duration.MEDIUM_1, 1, 0)); - - if (currentPage != null) { - setPageSize(currentPage); - currentPage.init(); - } - } - - public void setCurrentPage(Class clazz) { - - SimplePage page = getPage(clazz); - - if (page != null) { - setCurrentPage(page); - } - } - - public SimplePage getPage(Class clazz) { - - SimplePage page = null; - - for (SimplePage p : pages) { - if (p.getClass().equals(clazz)) { - page = p; - break; - } - } - - return page; - } - - public List getPages() { - return pages; - } - - public boolean isClosable() { - return closable; - } - - public void setClosable(boolean closable) { - this.closable = closable; - } - - public abstract List createPages(); - - public abstract float getX(); - - public abstract float getY(); - - public abstract float getWidth(); - - public abstract float getHeight(); -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/GuiTransition.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/GuiTransition.java deleted file mode 100644 index f56b2e9..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/GuiTransition.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api.page; - -import cn.pupperclient.animation.Animation; - -public abstract class GuiTransition { - - private final boolean consecutive; - - public GuiTransition(boolean consecutive) { - this.consecutive = consecutive; - } - - public abstract float[] onTransition(Animation animation); - - public boolean isConsecutive() { - return consecutive; - } -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/Page.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/Page.java deleted file mode 100644 index 31fffc0..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/Page.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api.page; - -import cn.pupperclient.gui.api.page.impl.api.SoarGui; -import cn.pupperclient.skia.Skia; -import cn.pupperclient.ui.component.impl.text.SearchBar; -import cn.pupperclient.utils.mouse.ScrollHelper; - -public class Page extends SimplePage { - - protected ScrollHelper scrollHelper = new ScrollHelper(); - protected SearchBar searchBar; - - public Page(SoarGui parent, String title, String icon, GuiTransition transition) { - super(parent, title, icon, transition); - } - - @Override - public void init() { - - String text = ""; - - if (searchBar != null) { - text = searchBar.getText(); - } - - searchBar = new SearchBar(x + width - 260 - 32, y + 32, 260, text, () -> { - scrollHelper.reset(); - }); - } - - @Override - public void draw(double mouseX, double mouseY) { - - scrollHelper.onUpdate(); - - Skia.save(); - Skia.translate(0, scrollHelper.getValue()); - - mouseY = (int) (mouseY - scrollHelper.getValue()); - searchBar.draw(mouseX, mouseY); - - Skia.restore(); - } - - @Override - public void mousePressed(double mouseX, double mouseY, int button) { - mouseY = mouseY - scrollHelper.getValue(); - searchBar.mousePressed(mouseX, mouseY, button); - } - - @Override - public void mouseReleased(double mouseX, double mouseY, int button) { - mouseY = mouseY - scrollHelper.getValue(); - searchBar.mouseReleased(mouseX, mouseY, button); - } - - @Override - public void charTyped(char chr, int modifiers) { - searchBar.charTyped(chr, modifiers); - } - - @Override - public void keyPressed(int keyCode, int scanCode, int modifiers) { - searchBar.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public void mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { - scrollHelper.onScroll(verticalAmount); - } -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/SimplePage.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/SimplePage.java deleted file mode 100644 index b0ce1d6..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/SimplePage.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api.page; - -import cn.pupperclient.animation.Animation; -import cn.pupperclient.animation.other.DummyAnimation; -import cn.pupperclient.gui.api.page.impl.api.SoarGui; - -public class SimplePage { - - protected float x, y, width, height; - private String title, icon; - protected SoarGui parent; - private Animation animation; - private GuiTransition transition; - - public SimplePage(SoarGui parent, String title, String icon, GuiTransition transition) { - this.parent = parent; - this.title = title; - this.icon = icon; - this.x = 0; - this.y = 0; - this.width = 0; - this.height = 0; - this.transition = transition; - this.animation = new DummyAnimation(1); - } - - public void init() { - } - - public void draw(double mouseX, double mouseY) { - } - - public void mousePressed(double mouseX, double mouseY, int button) { - } - - public void mouseReleased(double mouseX, double mouseY, int button) { - } - - public void mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { - } - - public void charTyped(char chr, int modifiers) { - } - - public void keyPressed(int keyCode, int scanCode, int modifiers) { - } - - public void onClosed() { - } - - public float getX() { - return x; - } - - public void setX(float x) { - this.x = x; - } - - public float getY() { - return y; - } - - public void setY(float y) { - this.y = y; - } - - public float getWidth() { - return width; - } - - public void setWidth(float width) { - this.width = width; - } - - public float getHeight() { - return height; - } - - public void setHeight(float height) { - this.height = height; - } - - public String getTitle() { - return title; - } - - public String getIcon() { - return icon; - } - - public GuiTransition getTransition() { - return transition; - } - - public void setTransition(GuiTransition transition) { - this.transition = transition; - } - - public Animation getAnimation() { - return animation; - } - - public void setAnimation(Animation animation) { - this.animation = animation; - } - - public SoarGui getParent() { - return parent; - } -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftRightTransition.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftRightTransition.java deleted file mode 100644 index b082c09..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftRightTransition.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api.page.impl; - -import cn.pupperclient.animation.Animation; -import cn.pupperclient.gui.api.page.impl.api.page.GuiTransition; - -public class LeftRightTransition extends GuiTransition { - - public LeftRightTransition(boolean consecutive) { - super(consecutive); - } - - @Override - public float[] onTransition(Animation animation) { - - float progress = animation.getValue(); - float x = 0; - - if (animation.getEnd() == 1) { - x = -1 + progress; - } else { - x = -1 + progress; - } - - return new float[] { x, 0 }; - } -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftTransition.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftTransition.java deleted file mode 100644 index 8042623..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/LeftTransition.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api.page.impl; - -import cn.pupperclient.animation.Animation; -import cn.pupperclient.gui.api.page.impl.api.page.GuiTransition; - -public class LeftTransition extends GuiTransition { - - public LeftTransition(boolean consecutive) { - super(consecutive); - } - - @Override - public float[] onTransition(Animation animation) { - - float progress = animation.getValue(); - float x = 0; - - if (animation.getEnd() == 1) { - x = -1 + progress; - } else { - x = -1 + progress; - } - - return new float[] { x, 0 }; - } -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightLeftTransition.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightLeftTransition.java deleted file mode 100644 index 9c3efc7..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightLeftTransition.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api.page.impl; - -import cn.pupperclient.animation.Animation; -import cn.pupperclient.gui.api.page.impl.api.page.GuiTransition; - -public class RightLeftTransition extends GuiTransition { - - public RightLeftTransition(boolean consecutive) { - super(consecutive); - } - - @Override - public float[] onTransition(Animation animation) { - - float progress = animation.getValue(); - float x = 0; - - if (animation.getEnd() == 1) { - x = 1 - progress; - } else { - x = -1 + progress; - } - - return new float[] { x, 0 }; - } -} diff --git a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightTransition.java b/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightTransition.java deleted file mode 100644 index 38d9164..0000000 --- a/src/main/java/cn/pupperclient/gui/api/page/impl/api/page/impl/RightTransition.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.pupperclient.gui.api.page.impl.api.page.impl; - -import cn.pupperclient.animation.Animation; -import cn.pupperclient.gui.api.page.impl.api.page.GuiTransition; - -public class RightTransition extends GuiTransition { - - public RightTransition(boolean consecutive) { - super(consecutive); - } - - @Override - public float[] onTransition(Animation animation) { - - float progress = animation.getValue(); - float x = 0; - - if (animation.getEnd() == 1) { - x = 1 - progress; - } else { - x = 1 + -progress; - } - - return new float[] { x, 0 }; - } -} From 358b4d6399aadda690a1481f1114e817f7d0785b Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 14:43:02 +0800 Subject: [PATCH 06/21] feat: Update shader for Minecraft 1.21.5 --- .../mixin/interfaces/IRenderPipeline.java | 7 + .../shader/ExtendedRenderPipelineBuilder.java | 27 +++ .../cn/pupperclient/shader/Framebuffer.java | 95 +++----- .../cn/pupperclient/shader/MeshBuilder.java | 100 +++++++++ .../shader/PostProcessRenderer.java | 184 ---------------- .../shader/PupperRenderPipelines.java | 105 +++++++++ .../java/cn/pupperclient/shader/Shader.java | 111 ---------- .../cn/pupperclient/shader/ShaderHelper.java | 203 ------------------ .../pupperclient/shader/impl/Kawaseblur.java | 126 ++++++----- .../skia/context/SkiaContext.java | 23 +- .../utils/mouse/ScrollHelper.java | 2 +- .../utils/server/ServerUtils.java | 6 +- .../resources/assets/pupper/shaders/blur.vert | 2 +- .../assets/pupper/shaders/blur_down.frag | 12 +- src/main/resources/pupper.accesswidener | 3 + 15 files changed, 370 insertions(+), 636 deletions(-) create mode 100644 src/main/java/cn/pupperclient/mixin/interfaces/IRenderPipeline.java create mode 100644 src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java create mode 100644 src/main/java/cn/pupperclient/shader/MeshBuilder.java delete mode 100644 src/main/java/cn/pupperclient/shader/PostProcessRenderer.java create mode 100644 src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java delete mode 100644 src/main/java/cn/pupperclient/shader/Shader.java delete mode 100644 src/main/java/cn/pupperclient/shader/ShaderHelper.java diff --git a/src/main/java/cn/pupperclient/mixin/interfaces/IRenderPipeline.java b/src/main/java/cn/pupperclient/mixin/interfaces/IRenderPipeline.java new file mode 100644 index 0000000..75839f8 --- /dev/null +++ b/src/main/java/cn/pupperclient/mixin/interfaces/IRenderPipeline.java @@ -0,0 +1,7 @@ +package cn.pupperclient.mixin.interfaces; + +public interface IRenderPipeline { + void pupper$setLineSmooth(boolean lineSmooth); + + boolean pupper$getLineSmooth(); +} diff --git a/src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java b/src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java new file mode 100644 index 0000000..49ab38c --- /dev/null +++ b/src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java @@ -0,0 +1,27 @@ +package cn.pupperclient.shader; + +import cn.pupperclient.mixin.interfaces.IRenderPipeline; +import com.mojang.blaze3d.pipeline.RenderPipeline; + +public class ExtendedRenderPipelineBuilder extends RenderPipeline.Builder { + private boolean lineSmooth; + + public ExtendedRenderPipelineBuilder(RenderPipeline.Snippet... snippets) { + for (RenderPipeline.Snippet snippet : snippets) { + withSnippet(snippet); + } + } + + public ExtendedRenderPipelineBuilder withLineSmooth() { + lineSmooth = true; + return this; + } + + @Override + public RenderPipeline build() { + RenderPipeline pipeline = super.build(); + ((IRenderPipeline) pipeline).pupper$setLineSmooth(lineSmooth); + + return pipeline; + } +} diff --git a/src/main/java/cn/pupperclient/shader/Framebuffer.java b/src/main/java/cn/pupperclient/shader/Framebuffer.java index c8d51f4..fa82d0a 100644 --- a/src/main/java/cn/pupperclient/shader/Framebuffer.java +++ b/src/main/java/cn/pupperclient/shader/Framebuffer.java @@ -1,24 +1,17 @@ package cn.pupperclient.shader; -import static org.lwjgl.opengl.GL11C.*; -import static org.lwjgl.opengl.GL12C.GL_CLAMP_TO_EDGE; -import static org.lwjgl.opengl.GL30C.GL_COLOR_ATTACHMENT0; -import static org.lwjgl.opengl.GL30C.GL_FRAMEBUFFER; -import static org.lwjgl.opengl.GL30C.glGenerateMipmap; - -import org.lwjgl.opengl.GL30C; - -import com.mojang.blaze3d.platform.GlStateManager; - +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.AddressMode; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.GpuTexture; +import com.mojang.blaze3d.textures.TextureFormat; import net.minecraft.client.MinecraftClient; import net.minecraft.client.util.Window; public class Framebuffer { - - private int id; - public int texture; + private GpuTexture texture; public double sizeMulti = 1; - public int width, height; + private FilterMode filterMode = FilterMode.LINEAR; private boolean mipmapEnabled = false; public Framebuffer(double sizeMulti) { @@ -33,72 +26,46 @@ public Framebuffer() { private void init() { Window window = MinecraftClient.getInstance().getWindow(); - id = GlStateManager.glGenFramebuffers(); - bind(); - - texture = GlStateManager._genTexture(); - ShaderHelper.bindTexture(texture); - ShaderHelper.defaultPixelStore(); - - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + int width = Math.max(1, (int) (window.getFramebufferWidth() * sizeMulti)); + int height = Math.max(1, (int) (window.getFramebufferHeight() * sizeMulti)); - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + texture = RenderSystem.getDevice().createTexture( + () -> "PupperFramebuffer", + TextureFormat.RGBA8, + width, + height, + 1 + ); - width = Math.max(1, (int) (window.getFramebufferWidth() * sizeMulti)); - height = Math.max(1, (int) (window.getFramebufferHeight() * sizeMulti)); - - ShaderHelper.textureImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null); - ShaderHelper.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); - - unbind(); + texture.setTextureFilter(filterMode, false); + texture.setAddressMode(AddressMode.CLAMP_TO_EDGE); } public void enableMipmap() { if (sizeMulti < 1.0) { mipmapEnabled = true; - bind(); - ShaderHelper.bindTexture(texture); - - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ShaderHelper.textureParam(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - unbind(); + texture.setTextureFilter(FilterMode.LINEAR, true); + texture.setAddressMode(AddressMode.CLAMP_TO_EDGE); } } - public void generateMipmap() { - if (mipmapEnabled && sizeMulti < 1.0) { - bind(); - ShaderHelper.bindTexture(texture); - // 直接使用原生的OpenGL调用生成Mipmap - glGenerateMipmap(GL_TEXTURE_2D); - unbind(); - } - } - - public void bind() { - GlStateManager._glBindFramebuffer(GL30C.GL_FRAMEBUFFER, id); - } - - public void setViewport() { - ShaderHelper.viewport(0, 0, width, height); - } - - public void unbind() { - MinecraftClient.getInstance().getFramebuffer().beginWrite(false); + public GpuTexture getTexture() { + return texture; } public void resize() { - GlStateManager._glDeleteFramebuffers(id); - GlStateManager._deleteTexture(texture); + if (texture != null) { + texture.close(); + } init(); if (mipmapEnabled) { enableMipmap(); } } + + public void close() { + if (texture != null) { + texture.close(); + } + } } diff --git a/src/main/java/cn/pupperclient/shader/MeshBuilder.java b/src/main/java/cn/pupperclient/shader/MeshBuilder.java new file mode 100644 index 0000000..fd42c18 --- /dev/null +++ b/src/main/java/cn/pupperclient/shader/MeshBuilder.java @@ -0,0 +1,100 @@ +package cn.pupperclient.shader; + +import com.mojang.blaze3d.buffers.BufferType; +import com.mojang.blaze3d.buffers.BufferUsage; +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import org.lwjgl.BufferUtils; + +import java.nio.ByteBuffer; +import static org.lwjgl.system.MemoryUtil.*; + +public class MeshBuilder { + private final VertexFormat format; + private final VertexFormat.DrawMode drawMode; + + private ByteBuffer vertices; + private long verticesPointerStart, verticesPointer; + + private ByteBuffer indices; + private long indicesPointer; + + private int vertexI, indicesCount; + private boolean building; + + public MeshBuilder(VertexFormat format, VertexFormat.DrawMode drawMode) { + this.format = format; + this.drawMode = drawMode; + + int vertexSize = format.getVertexSize(); + vertices = BufferUtils.createByteBuffer(vertexSize * 256 * 4); + verticesPointerStart = memAddress0(vertices); + + indices = BufferUtils.createByteBuffer(6 * 512 * 4); // 假设四边形 + indicesPointer = memAddress0(indices); + } + + public void begin() { + if (building) return; + + verticesPointer = verticesPointerStart; + vertexI = 0; + indicesCount = 0; + building = true; + } + + public MeshBuilder vec2(float x, float y) { + long p = verticesPointer; + memPutFloat(p, x); + memPutFloat(p + 4, y); + verticesPointer += 8; + return this; + } + + public int next() { + return vertexI++; + } + + public void quad(int i1, int i2, int i3, int i4) { + long p = indicesPointer + indicesCount * 4L; + memPutInt(p, i1); + memPutInt(p + 4, i2); + memPutInt(p + 8, i3); + memPutInt(p + 12, i3); + memPutInt(p + 16, i4); + memPutInt(p + 20, i1); + indicesCount += 6; + } + + public void end() { + if (!building) return; + building = false; + } + + public GpuBuffer createVertexBuffer() { + int size = (int) (verticesPointer - verticesPointerStart); + vertices.limit(size); + + return RenderSystem.getDevice().createBuffer( + () -> "MeshVertexBuffer", + BufferType.VERTICES, + BufferUsage.DYNAMIC_WRITE, + vertices + ); + } + + public GpuBuffer createIndexBuffer() { + indices.limit(indicesCount * 4); + return RenderSystem.getDevice().createBuffer( + () -> "MeshIndexBuffer", + BufferType.INDICES, + BufferUsage.DYNAMIC_WRITE, + indices + ); + } + + public int getIndicesCount() { + return indicesCount; + } +} diff --git a/src/main/java/cn/pupperclient/shader/PostProcessRenderer.java b/src/main/java/cn/pupperclient/shader/PostProcessRenderer.java deleted file mode 100644 index a2a413b..0000000 --- a/src/main/java/cn/pupperclient/shader/PostProcessRenderer.java +++ /dev/null @@ -1,184 +0,0 @@ -package cn.pupperclient.shader; - -import static org.lwjgl.opengl.GL11C.GL_FLOAT; -import static org.lwjgl.opengl.GL11C.GL_UNSIGNED_INT; -import static org.lwjgl.opengl.GL15C.GL_ARRAY_BUFFER; -import static org.lwjgl.opengl.GL15C.GL_DYNAMIC_DRAW; -import static org.lwjgl.opengl.GL15C.GL_ELEMENT_ARRAY_BUFFER; -import static org.lwjgl.system.MemoryUtil.memAddress0; -import static org.lwjgl.system.MemoryUtil.memPutFloat; -import static org.lwjgl.system.MemoryUtil.memPutInt; - -import java.nio.ByteBuffer; - -import org.lwjgl.BufferUtils; -import org.lwjgl.opengl.GL15C; -import org.lwjgl.opengl.GL32C; - -import com.mojang.blaze3d.platform.GlStateManager; - -import net.minecraft.client.util.math.MatrixStack; - -public class PostProcessRenderer { - - private static final Mesh mesh = new Mesh(); - private static final MatrixStack matrices = new MatrixStack(); - - static { - mesh.begin(); - mesh.quad(mesh.vec2(-1, -1).next(), mesh.vec2(-1, 1).next(), mesh.vec2(1, 1).next(), mesh.vec2(1, -1).next()); - mesh.end(); - } - - public static void beginRender() { - mesh.beginRender(matrices); - } - - public static void render() { - mesh.render(matrices); - } - - public static void endRender() { - mesh.endRender(); - } - - private static class Mesh { - - private final int vao, vbo, ibo; - - private final ByteBuffer vertices; - private final long verticesPointerStart; - private long verticesPointer; - - private final ByteBuffer indices; - private final long indicesPointer; - - private int vertexI, indicesCount; - - private boolean building; - private boolean beganRendering; - - public Mesh() { - int drawMode = 3; - int stride = 8; - - int primitiveVerticesSize = stride * drawMode; - - vertices = BufferUtils.createByteBuffer(primitiveVerticesSize * 256 * 4); - verticesPointerStart = memAddress0(vertices); - - indices = BufferUtils.createByteBuffer(drawMode * 512 * 4); - indicesPointer = memAddress0(indices); - - vao = GlStateManager._glGenVertexArrays(); - ShaderHelper.bindVertexArray(vao); - - vbo = GlStateManager._glGenBuffers(); - GlStateManager._glBindBuffer(GL15C.GL_ARRAY_BUFFER, vbo); - - ibo = GlStateManager._glGenBuffers(); - ShaderHelper.bindIndexBuffer(ibo); - GlStateManager._enableVertexAttribArray(0); - GlStateManager._vertexAttribPointer(0, 2, GL_FLOAT, false, stride, 0); - - ShaderHelper.bindVertexArray(0); - GlStateManager._glBindBuffer(GL15C.GL_ARRAY_BUFFER, 0); - ShaderHelper.bindIndexBuffer(0); - } - - public void begin() { - - if (building) { - return; - } - - verticesPointer = verticesPointerStart; - vertexI = 0; - indicesCount = 0; - - building = true; - } - - public Mesh vec2(double x, double y) { - long p = verticesPointer; - - memPutFloat(p, (float) x); - memPutFloat(p + 4, (float) y); - - verticesPointer += 8; - return this; - } - - public int next() { - return vertexI++; - } - - public void quad(int i1, int i2, int i3, int i4) { - long p = indicesPointer + indicesCount * 4L; - - memPutInt(p, i1); - memPutInt(p + 4, i2); - memPutInt(p + 8, i3); - - memPutInt(p + 12, i3); - memPutInt(p + 16, i4); - memPutInt(p + 20, i1); - - indicesCount += 6; - } - - public void end() { - - if (!building) { - return; - } - - if (indicesCount > 0) { - GlStateManager._glBindBuffer(GL15C.GL_ARRAY_BUFFER, vbo); - GlStateManager._glBufferData(GL_ARRAY_BUFFER, vertices.limit(getVerticesOffset()), GL_DYNAMIC_DRAW); - GlStateManager._glBindBuffer(GL15C.GL_ARRAY_BUFFER, 0); - - ShaderHelper.bindIndexBuffer(ibo); - GlStateManager._glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.limit(indicesCount * 4), GL_DYNAMIC_DRAW); - ShaderHelper.bindIndexBuffer(0); - } - - building = false; - } - - public void beginRender(MatrixStack matrices) { - ShaderHelper.disableCull(); - - beganRendering = true; - } - - public void render(MatrixStack matrices) { - if (building) - end(); - - if (indicesCount > 0) { - boolean wasBeganRendering = beganRendering; - if (!wasBeganRendering) - beginRender(matrices); - - Shader.BOUND.setDefaults(); - - ShaderHelper.bindVertexArray(vao); - GlStateManager._drawElements(GL32C.GL_TRIANGLES, indicesCount, GL_UNSIGNED_INT, 0); - - ShaderHelper.bindVertexArray(0); - - if (!wasBeganRendering) - endRender(); - } - } - - public void endRender() { - beganRendering = false; - } - - private int getVerticesOffset() { - return (int) (verticesPointer - verticesPointerStart); - } - } -} diff --git a/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java b/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java new file mode 100644 index 0000000..a92847a --- /dev/null +++ b/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java @@ -0,0 +1,105 @@ +package cn.pupperclient.shader; + +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.UniformType; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.SynchronousResourceReloader; +import net.minecraft.util.Identifier; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class PupperRenderPipelines { + private static final List PIPELINES = new ArrayList<>(); + + public static final VertexFormat POS2 = VertexFormat.builder() + .add("POS2", VertexFormatElement.POSITION) + .build(); + + public static final VertexFormat POS2_TEXTURE = VertexFormat.builder() + .add("POS2_TEXTURE", VertexFormatElement.POSITION) + .add("UV0", VertexFormatElement.UV0) + .build(); + + // 着色器管线定义 + public static final RenderPipeline BLUR_DOWN = register(new RenderPipeline.Builder() + .withVertexShader(read("blur.vert")) + .withFragmentShader(read("blur_down.frag")) + .withVertexFormat(POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withSampler("uTexture") + .withUniform("uHalfTexelSize", UniformType.VEC2) + .withUniform("uOffset", UniformType.FLOAT) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withDepthWrite(false) + .withBlend(BlendFunction.TRANSLUCENT) + .withCull(false) + .build()); + + public static final RenderPipeline BLUR_UP = register(new RenderPipeline.Builder() + .withVertexShader(read("blur.vert")) + .withFragmentShader(read("blur_up.frag")) + .withVertexFormat(POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withSampler("uTexture") + .withUniform("uHalfTexelSize", UniformType.VEC2) + .withUniform("uOffset", UniformType.FLOAT) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withDepthWrite(false) + .withBlend(BlendFunction.TRANSLUCENT) + .withCull(false) + .build()); + + public static final RenderPipeline PASSTHROUGH = register(new RenderPipeline.Builder() + .withVertexShader(read("passthrough.vert")) + .withFragmentShader(read("passthrough.frag")) + .withVertexFormat(POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withSampler("uTexture") + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withDepthWrite(false) + .withBlend(BlendFunction.TRANSLUCENT) + .withCull(false) + .build()); + + private static RenderPipeline register(RenderPipeline pipeline) { + PIPELINES.add(pipeline); + return pipeline; + } + + public class Reloader implements SynchronousResourceReloader { + @Override + public void reload(ResourceManager manager) { + GpuDevice device = RenderSystem.getDevice(); + + for (RenderPipeline pipeline : PIPELINES) { + device.precompilePipeline(pipeline, (identifier, shaderType) -> { + var resource = manager.getResource(identifier).get(); + try (var in = resource.getInputStream()) { + return IOUtils.toString(in, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + } + + private static String read(String path) { + try { + return IOUtils.toString( + MinecraftClient.getInstance().getResourceManager() + .getResource(Identifier.of("pupper", "shaders/" + path)).get().getInputStream(), + StandardCharsets.UTF_8); + } catch (IOException e) { + throw new IllegalStateException("Could not read shader '" + path + "'", e); + } + } +} diff --git a/src/main/java/cn/pupperclient/shader/Shader.java b/src/main/java/cn/pupperclient/shader/Shader.java deleted file mode 100644 index ba9e22c..0000000 --- a/src/main/java/cn/pupperclient/shader/Shader.java +++ /dev/null @@ -1,111 +0,0 @@ -package cn.pupperclient.shader; - -import static org.lwjgl.opengl.GL11C.GL_FALSE; -import static org.lwjgl.opengl.GL11C.GL_TRUE; -import static org.lwjgl.opengl.GL20C.GL_FRAGMENT_SHADER; -import static org.lwjgl.opengl.GL20C.GL_VERTEX_SHADER; - -import java.io.IOException; -import java.nio.FloatBuffer; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.IOUtils; -import org.joml.Matrix4f; -import org.lwjgl.BufferUtils; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import net.minecraft.client.MinecraftClient; -import net.minecraft.util.Identifier; - -public class Shader { - - private static final FloatBuffer MAT = BufferUtils.createFloatBuffer(4 * 4); - - public static Shader BOUND; - - private final int id; - private final Object2IntMap uniformLocations = new Object2IntOpenHashMap<>(); - - public Shader(String vertPath, String fragPath) { - int vert = GlStateManager.glCreateShader(GL_VERTEX_SHADER); - GlStateManager.glShaderSource(vert, read(vertPath)); - - String vertError = ShaderHelper.compileShader(vert); - if (vertError != null) { - throw new RuntimeException("Failed to compile vertex shader: " + vertError); - } - - int frag = GlStateManager.glCreateShader(GL_FRAGMENT_SHADER); - GlStateManager.glShaderSource(frag, read(fragPath)); - - String fragError = ShaderHelper.compileShader(frag); - if (fragError != null) { - throw new RuntimeException("Failed to compile fragment shader: " + fragError); - } - - id = GlStateManager.glCreateProgram(); - - String programError = ShaderHelper.linkProgram(id, vert, frag); - if (programError != null) { - throw new RuntimeException("Failed to link program: " + programError); - } - - GlStateManager.glDeleteShader(vert); - GlStateManager.glDeleteShader(frag); - } - - private String read(String path) { - try { - return IOUtils.toString( - MinecraftClient.getInstance().getResourceManager() - .getResource(Identifier.of("pupper", "shaders/" + path)).get().getInputStream(), - StandardCharsets.UTF_8); - } catch (IOException e) { - throw new IllegalStateException("Could not read shader '" + path + "'", e); - } - } - - public void bind() { - ShaderHelper.useProgram(id); - BOUND = this; - } - - private int getLocation(String name) { - if (uniformLocations.containsKey(name)) - return uniformLocations.getInt(name); - - int location = ShaderHelper.getUniformLocation(id, name); - uniformLocations.put(name, location); - return location; - } - - public void set(String name, boolean v) { - ShaderHelper.uniformInt(getLocation(name), v ? GL_TRUE : GL_FALSE); - } - - public void set(String name, int v) { - ShaderHelper.uniformInt(getLocation(name), v); - } - - public void set(String name, double v) { - ShaderHelper.uniformFloat(getLocation(name), (float) v); - } - - public void set(String name, double v1, double v2) { - ShaderHelper.uniformFloat2(getLocation(name), (float) v1, (float) v2); - } - - public void set(String name, Matrix4f mat) { - mat.get(MAT); - GlStateManager._glUniformMatrix4(getLocation(name), false, MAT); - } - - public void setDefaults() { - set("u_Proj", RenderSystem.getProjectionMatrix()); - set("u_ModelView", RenderSystem.getModelViewStack()); - } -} diff --git a/src/main/java/cn/pupperclient/shader/ShaderHelper.java b/src/main/java/cn/pupperclient/shader/ShaderHelper.java deleted file mode 100644 index 6868632..0000000 --- a/src/main/java/cn/pupperclient/shader/ShaderHelper.java +++ /dev/null @@ -1,203 +0,0 @@ -package cn.pupperclient.shader; - -import static org.lwjgl.opengl.GL11C.GL_FALSE; -import static org.lwjgl.opengl.GL11C.GL_LINE_SMOOTH; -import static org.lwjgl.opengl.GL11C.GL_ONE_MINUS_SRC_ALPHA; -import static org.lwjgl.opengl.GL11C.GL_SRC_ALPHA; -import static org.lwjgl.opengl.GL11C.GL_UNPACK_ALIGNMENT; -import static org.lwjgl.opengl.GL11C.GL_UNPACK_LSB_FIRST; -import static org.lwjgl.opengl.GL11C.GL_UNPACK_ROW_LENGTH; -import static org.lwjgl.opengl.GL11C.GL_UNPACK_SKIP_PIXELS; -import static org.lwjgl.opengl.GL11C.GL_UNPACK_SKIP_ROWS; -import static org.lwjgl.opengl.GL11C.GL_UNPACK_SWAP_BYTES; -import static org.lwjgl.opengl.GL11C.glDisable; -import static org.lwjgl.opengl.GL11C.glEnable; -import static org.lwjgl.opengl.GL11C.glLineWidth; -import static org.lwjgl.opengl.GL11C.glTexImage2D; -import static org.lwjgl.opengl.GL12C.GL_UNPACK_IMAGE_HEIGHT; -import static org.lwjgl.opengl.GL12C.GL_UNPACK_SKIP_IMAGES; -import static org.lwjgl.opengl.GL13C.GL_TEXTURE0; -import static org.lwjgl.opengl.GL15C.GL_ELEMENT_ARRAY_BUFFER; -import static org.lwjgl.opengl.GL20C.GL_COMPILE_STATUS; -import static org.lwjgl.opengl.GL20C.GL_LINK_STATUS; -import static org.lwjgl.opengl.GL20C.glUniform1f; -import static org.lwjgl.opengl.GL20C.glUniform2f; -import static org.lwjgl.opengl.GL20C.glUniform3f; -import static org.lwjgl.opengl.GL20C.glUniform3fv; -import static org.lwjgl.opengl.GL20C.glUniform4f; -import static org.lwjgl.opengl.GL30C.glGenerateMipmap; - -import java.nio.ByteBuffer; - -import com.mojang.blaze3d.platform.GlStateManager; -import cn.pupperclient.mixin.mixins.minecraft.client.render.BufferRendererAccessor; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.texture.AbstractTexture; -import net.minecraft.util.Identifier; - -public class ShaderHelper { - - public static int CURRENT_IBO; - private static int prevIbo; - - private ShaderHelper() { - } - - public static void bindVertexArray(int vao) { - GlStateManager._glBindVertexArray(vao); - BufferRendererAccessor.setCurrentVertexBuffer(null); - } - - public static void bindIndexBuffer(int ibo) { - if (ibo != 0) - prevIbo = CURRENT_IBO; - GlStateManager._glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo != 0 ? ibo : prevIbo); - } - - public static String compileShader(int shader) { - GlStateManager.glCompileShader(shader); - - if (GlStateManager.glGetShaderi(shader, GL_COMPILE_STATUS) == GL_FALSE) { - return GlStateManager.glGetShaderInfoLog(shader, 512); - } - - return null; - } - - public static String linkProgram(int program, int vertShader, int fragShader) { - GlStateManager.glAttachShader(program, vertShader); - GlStateManager.glAttachShader(program, fragShader); - GlStateManager.glLinkProgram(program); - - if (GlStateManager.glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) { - return GlStateManager.glGetProgramInfoLog(program, 512); - } - - return null; - } - - public static void useProgram(int program) { - GlStateManager._glUseProgram(program); - } - - public static void viewport(int x, int y, int width, int height) { - GlStateManager._viewport(x, y, width, height); - } - - public static int getUniformLocation(int program, String name) { - return GlStateManager._glGetUniformLocation(program, name); - } - - public static void uniformInt(int location, int v) { - GlStateManager._glUniform1i(location, v); - } - - public static void uniformFloat(int location, float v) { - glUniform1f(location, v); - } - - public static void uniformFloat2(int location, float v1, float v2) { - glUniform2f(location, v1, v2); - } - - public static void uniformFloat3(int location, float v1, float v2, float v3) { - glUniform3f(location, v1, v2, v3); - } - - public static void uniformFloat4(int location, float v1, float v2, float v3, float v4) { - glUniform4f(location, v1, v2, v3, v4); - } - - public static void uniformFloat3Array(int location, float[] v) { - glUniform3fv(location, v); - } - - public static void pixelStore(int name, int param) { - GlStateManager._pixelStore(name, param); - } - - public static void textureParam(int target, int name, int param) { - GlStateManager._texParameter(target, name, param); - } - - public static void textureImage2D(int target, int level, int internalFormat, int width, int height, int border, - int format, int type, ByteBuffer pixels) { - glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels); - } - - public static void defaultPixelStore() { - pixelStore(GL_UNPACK_SWAP_BYTES, GL_FALSE); - pixelStore(GL_UNPACK_LSB_FIRST, GL_FALSE); - pixelStore(GL_UNPACK_ROW_LENGTH, 0); - pixelStore(GL_UNPACK_IMAGE_HEIGHT, 0); - pixelStore(GL_UNPACK_SKIP_ROWS, 0); - pixelStore(GL_UNPACK_SKIP_PIXELS, 0); - pixelStore(GL_UNPACK_SKIP_IMAGES, 0); - pixelStore(GL_UNPACK_ALIGNMENT, 4); - } - - public static void framebufferTexture2D(int target, int attachment, int textureTarget, int texture, int level) { - GlStateManager._glFramebufferTexture2D(target, attachment, textureTarget, texture, level); - } - - public static void enableDepth() { - GlStateManager._enableDepthTest(); - } - - public static void disableDepth() { - GlStateManager._disableDepthTest(); - } - - public static void enableBlend() { - GlStateManager._enableBlend(); - GlStateManager._blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - public static void disableBlend() { - GlStateManager._disableBlend(); - } - - public static void enableCull() { - GlStateManager._enableCull(); - } - - public static void disableCull() { - GlStateManager._disableCull(); - } - - public static void enableLineSmooth() { - glEnable(GL_LINE_SMOOTH); - glLineWidth(1); - } - - public static void disableLineSmooth() { - glDisable(GL_LINE_SMOOTH); - } - - public static void bindTexture(Identifier id) { - AbstractTexture texture = MinecraftClient.getInstance().getTextureManager().getTexture(id); - bindTexture(texture.getGlId(), 0); - } - - public static void bindTexture(int i, int slot) { - GlStateManager._activeTexture(GL_TEXTURE0 + slot); - GlStateManager._bindTexture(i); - } - - public static void bindTexture(int i) { - bindTexture(i, 0); - } - - public static void resetTextureSlot() { - GlStateManager._activeTexture(GL_TEXTURE0); - } - - public static void generateMipmap(int target) { - glGenerateMipmap(target); - } - - public static void textureParam(int target, int pname, float param) { - GlStateManager._texParameter(target, pname, param); - } -} diff --git a/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java b/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java index dffeb86..90aefb2 100644 --- a/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java +++ b/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java @@ -1,14 +1,20 @@ package cn.pupperclient.shader.impl; -import cn.pupperclient.management.mod.impl.settings.SystemSettings; -import cn.pupperclient.shader.*; - -import cn.pupperclient.utils.TimerUtils; +import cn.pupperclient.shader.Framebuffer; +import cn.pupperclient.shader.MeshBuilder; +import cn.pupperclient.shader.PupperRenderPipelines; +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.GpuTexture; +import com.mojang.blaze3d.vertex.VertexFormat; import it.unimi.dsi.fastutil.ints.IntDoubleImmutablePair; import net.minecraft.client.MinecraftClient; -public class Kawaseblur { +import java.util.OptionalInt; +public class Kawaseblur { public static final Kawaseblur GUI_BLUR = new Kawaseblur(); public static final Kawaseblur INGAME_BLUR = new Kawaseblur(); @@ -21,10 +27,9 @@ public class Kawaseblur { IntDoubleImmutablePair.of(5, 4.5), IntDoubleImmutablePair.of(5, 5.25), IntDoubleImmutablePair.of(5, 6.25), IntDoubleImmutablePair.of(5, 7.25), IntDoubleImmutablePair.of(5, 8.5) }; - private static Shader shaderDown, shaderUp, shaderPassthrough; private final Framebuffer[] fbos = new Framebuffer[6]; - private final TimerUtils timer = new TimerUtils(); private boolean firstTick = true; + private MeshBuilder mesh; public void resize() { for (int i = 0; i < fbos.length; i++) { @@ -37,68 +42,91 @@ public void resize() { } public void draw(int radius) { - - if (shaderDown == null) { - shaderDown = new Shader("blur.vert", "blur_down.frag"); - shaderUp = new Shader("blur.vert", "blur_up.frag"); - shaderPassthrough = new Shader("passthrough.vert", "passthrough.frag"); - } - if (firstTick) { for (int i = 0; i < fbos.length; i++) { if (fbos[i] == null) { fbos[i] = new Framebuffer(1 / Math.pow(2, i)); } } - firstTick = false; - } - SystemSettings setting = SystemSettings.getInstance(); + // 创建全屏四边形网格 + mesh = new MeshBuilder(PupperRenderPipelines.POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES); + mesh.begin(); + mesh.vec2(-1, -1).next(); + mesh.vec2(-1, 1).next(); + mesh.vec2(1, 1).next(); + mesh.vec2(1, -1).next(); + mesh.quad(0, 1, 2, 3); + mesh.end(); - if(setting.isFastBlur()) { - if (timer.delay(16)) { - timer.reset(); - } else { - return; - } + firstTick = false; } - IntDoubleImmutablePair strength = STRENGTHS[(int) ((radius - 1))]; + IntDoubleImmutablePair strength = STRENGTHS[Math.min(radius - 1, STRENGTHS.length - 1)]; int iterations = strength.leftInt(); double offset = strength.rightDouble(); - PostProcessRenderer.beginRender(); + GpuBuffer vertexBuffer = mesh.createVertexBuffer(); + GpuBuffer indexBuffer = mesh.createIndexBuffer(); + + try { + // 第一遍:降采样 + renderToFbo(fbos[0], + MinecraftClient.getInstance().getFramebuffer().getColorAttachment(), + PupperRenderPipelines.BLUR_DOWN, + vertexBuffer, indexBuffer, offset); + + // 多次降采样 + for (int i = 0; i < iterations; i++) { + renderToFbo(fbos[i + 1], + fbos[i].getTexture(), + PupperRenderPipelines.BLUR_DOWN, + vertexBuffer, indexBuffer, offset); + } + + // 多次上采样 + for (int i = iterations; i >= 1; i--) { + renderToFbo(fbos[i - 1], + fbos[i].getTexture(), + PupperRenderPipelines.BLUR_UP, + vertexBuffer, indexBuffer, offset); + } - renderToFbo(fbos[0], MinecraftClient.getInstance().getFramebuffer().getColorAttachment(), shaderDown, - offset); + // 最终绘制到屏幕 + RenderPass pass = RenderSystem.getDevice().createCommandEncoder() + .createRenderPass(MinecraftClient.getInstance().getFramebuffer().getColorAttachment(), OptionalInt.empty()); - for (int i = 0; i < iterations; i++) { - renderToFbo(fbos[i + 1], fbos[i].texture, shaderDown, offset); - } + pass.setPipeline(PupperRenderPipelines.PASSTHROUGH); + pass.bindSampler("uTexture", fbos[0].getTexture()); + pass.setVertexBuffer(0, vertexBuffer); + pass.setIndexBuffer(indexBuffer, VertexFormat.IndexType.INT); + pass.drawIndexed(0, mesh.getIndicesCount()); + pass.close(); - for (int i = iterations; i >= 1; i--) { - renderToFbo(fbos[i - 1], fbos[i].texture, shaderUp, offset); + } finally { + vertexBuffer.close(); + indexBuffer.close(); } - - MinecraftClient.getInstance().getFramebuffer().beginWrite(true); - shaderPassthrough.bind(); - ShaderHelper.bindTexture(fbos[0].texture); - shaderPassthrough.set("uTexture", 0); - PostProcessRenderer.endRender(); } - public int getTexture() { - return fbos[0].texture; + private void renderToFbo(Framebuffer target, GpuTexture source, + RenderPipeline pipeline, + GpuBuffer vertexBuffer, GpuBuffer indexBuffer, + double offset) { + RenderPass pass = RenderSystem.getDevice().createCommandEncoder() + .createRenderPass(target.getTexture(), OptionalInt.empty()); + + pass.setPipeline(pipeline); + pass.bindSampler("uTexture", source); + pass.setUniform("uHalfTexelSize", 0.5f / target.getTexture().getWidth(0), 0.5f / target.getTexture().getHeight(0)); + pass.setUniform("uOffset", (float) offset); + pass.setVertexBuffer(0, vertexBuffer); + pass.setIndexBuffer(indexBuffer, VertexFormat.IndexType.INT); + pass.drawIndexed(0, mesh.getIndicesCount()); + pass.close(); } - private void renderToFbo(Framebuffer targetFbo, int sourceText, Shader shader, double offset) { - targetFbo.bind(); - targetFbo.setViewport(); - shader.bind(); - ShaderHelper.bindTexture(sourceText); - shader.set("uTexture", 0); - shader.set("uHalfTexelSize", .5 / targetFbo.width, .5 / targetFbo.height); - shader.set("uOffset", offset); - PostProcessRenderer.render(); + public GpuTexture getTexture() { + return fbos[0].getTexture(); } } diff --git a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java index 3167791..2000124 100644 --- a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java +++ b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java @@ -6,7 +6,7 @@ import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL33; -import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.systems.RenderSystem; import io.github.humbleui.skija.BackendRenderTarget; @@ -17,7 +17,6 @@ import io.github.humbleui.skija.SurfaceColorFormat; import io.github.humbleui.skija.SurfaceOrigin; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.BufferRenderer; public class SkiaContext { @@ -65,26 +64,26 @@ public static void draw(Consumer drawingLogic) { context.flush(); - BufferRenderer.reset(); + // BufferRenderer.reset(); GL33.glBindSampler(0, 0); - RenderSystem.disableBlend(); + // RenderSystem.disableBlend(); GL11.glDisable(GL11.GL_BLEND); - RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); + // RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); - RenderSystem.blendEquation(GL33.GL_FUNC_ADD); + // RenderSystem.blendEquation(GL33.GL_FUNC_ADD); GL33.glBlendEquation(GL33.GL_FUNC_ADD); - RenderSystem.colorMask(true, true, true, true); + // RenderSystem.colorMask(true, true, true, true); GL11.glColorMask(true, true, true, true); - RenderSystem.depthMask(true); + // RenderSystem.depthMask(true); GL11.glDepthMask(true); - RenderSystem.disableScissor(); + //RenderSystem.disableScissor(); GL11.glDisable(GL11.GL_SCISSOR_TEST); GL11.glDisable(GL11.GL_STENCIL_TEST); - RenderSystem.disableDepthTest(); + // RenderSystem.disableDepthTest(); GL11.glDisable(GL11.GL_DEPTH_TEST); GL13.glActiveTexture(GL13.GL_TEXTURE0); - RenderSystem.activeTexture(GL13.GL_TEXTURE0); - RenderSystem.disableCull(); + //RenderSystem.activeTexture(GL13.GL_TEXTURE0); + //RenderSystem.disableCull(); } public static DirectContext getContext() { diff --git a/src/main/java/cn/pupperclient/utils/mouse/ScrollHelper.java b/src/main/java/cn/pupperclient/utils/mouse/ScrollHelper.java index 52d9adf..3edb892 100644 --- a/src/main/java/cn/pupperclient/utils/mouse/ScrollHelper.java +++ b/src/main/java/cn/pupperclient/utils/mouse/ScrollHelper.java @@ -13,7 +13,7 @@ public ScrollHelper() { } public void onScroll(double amount) { - scroll += amount * 60; + scroll += (float) (amount * 60); } public void onUpdate() { diff --git a/src/main/java/cn/pupperclient/utils/server/ServerUtils.java b/src/main/java/cn/pupperclient/utils/server/ServerUtils.java index 2bb082e..0c2c8fe 100644 --- a/src/main/java/cn/pupperclient/utils/server/ServerUtils.java +++ b/src/main/java/cn/pupperclient/utils/server/ServerUtils.java @@ -2,9 +2,11 @@ import net.minecraft.client.MinecraftClient; +import java.util.Objects; + public class ServerUtils { - private static MinecraftClient client = MinecraftClient.getInstance(); + private static final MinecraftClient client = MinecraftClient.getInstance(); public static boolean isJoin(Server server) { return isMultiplayer() && getAddress().contains(server.getAddress()); @@ -19,6 +21,6 @@ public static boolean isMultiplayer() { } public static String getAddress() { - return isMultiplayer() ? client.getCurrentServerEntry().address : "null"; + return isMultiplayer() ? Objects.requireNonNull(client.getCurrentServerEntry()).address : "null"; } } diff --git a/src/main/resources/assets/pupper/shaders/blur.vert b/src/main/resources/assets/pupper/shaders/blur.vert index f588849..ab7813c 100644 --- a/src/main/resources/assets/pupper/shaders/blur.vert +++ b/src/main/resources/assets/pupper/shaders/blur.vert @@ -8,4 +8,4 @@ out vec2 uv; void main() { gl_Position = vec4(pos, 0, 1); uv = pos * .5 + .5; -} +} \ No newline at end of file diff --git a/src/main/resources/assets/pupper/shaders/blur_down.frag b/src/main/resources/assets/pupper/shaders/blur_down.frag index 7f49298..75454f5 100644 --- a/src/main/resources/assets/pupper/shaders/blur_down.frag +++ b/src/main/resources/assets/pupper/shaders/blur_down.frag @@ -9,19 +9,13 @@ uniform sampler2D uTexture; uniform vec2 uHalfTexelSize; uniform float uOffset; -vec4 safeTexture2D(sampler2D tex, vec2 uv) { - uv = clamp(uv, 0.001, 0.999); // 防止在边缘采样 - return texture(tex, uv); -} - void main() { color = ( - texture(uTexture, uv) * 4 + + texture(uTexture, uv) * 4.0 + texture(uTexture, uv - uHalfTexelSize.xy * uOffset) + texture(uTexture, uv + uHalfTexelSize.xy * uOffset) + texture(uTexture, uv + vec2(uHalfTexelSize.x, -uHalfTexelSize.y) * uOffset) + texture(uTexture, uv - vec2(uHalfTexelSize.x, -uHalfTexelSize.y) * uOffset) - ) / 8; - color.a = 1; + ) / 8.0; + color.a = 1.0; } - diff --git a/src/main/resources/pupper.accesswidener b/src/main/resources/pupper.accesswidener index 8d73f3a..97879e6 100644 --- a/src/main/resources/pupper.accesswidener +++ b/src/main/resources/pupper.accesswidener @@ -36,3 +36,6 @@ accessible method net/minecraft/client/render/entity/LivingEntityRenderer addFea accessible field net/minecraft/client/render/entity/EntityRenderDispatcher renderers Ljava/util/Map; accessible field net/minecraft/client/render/entity/LivingEntityRenderer features Ljava/util/List; accessible field net/minecraft/client/render/entity/EntityRenderDispatcher modelRenderers Ljava/util/Map; + +accessible method com/mojang/blaze3d/pipeline/RenderPipeline$Builder ()V +accessible method com/mojang/blaze3d/pipeline/RenderPipeline$Builder withSnippet (Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet;)V From f2fe1b5d99657ada642047252180684a34ce9eff Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 14:48:05 +0800 Subject: [PATCH 07/21] fix: rename method --- .../mixin/interfaces/IMixinCameraEntity.java | 8 ++++---- .../mixin/interfaces/IMixinLivingEntity.java | 2 +- .../minecraft/client/MixinMinecraftClient.java | 2 +- .../client/render/BufferRendererAccessor.java | 14 -------------- .../minecraft/client/render/MixinCamera.java | 6 +++--- .../mixin/mixins/minecraft/entity/MixinEntity.java | 8 ++++---- .../mixins/minecraft/entity/MixinLivingEntity.java | 2 +- 7 files changed, 14 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/BufferRendererAccessor.java diff --git a/src/main/java/cn/pupperclient/mixin/interfaces/IMixinCameraEntity.java b/src/main/java/cn/pupperclient/mixin/interfaces/IMixinCameraEntity.java index 9ab481f..1a89268 100644 --- a/src/main/java/cn/pupperclient/mixin/interfaces/IMixinCameraEntity.java +++ b/src/main/java/cn/pupperclient/mixin/interfaces/IMixinCameraEntity.java @@ -1,9 +1,9 @@ package cn.pupperclient.mixin.interfaces; public interface IMixinCameraEntity { - float soarClient_CN$getCameraPitch(); - float soarClient_CN$getCameraYaw(); + float pupper$getCameraPitch(); + float pupper$getCameraYaw(); - void soarClient_CN$setCameraPitch(float pitch); - void soarClient_CN$setCameraYaw(float yaw); + void pupper$setCameraPitch(float pitch); + void pupper$setCameraYaw(float yaw); } diff --git a/src/main/java/cn/pupperclient/mixin/interfaces/IMixinLivingEntity.java b/src/main/java/cn/pupperclient/mixin/interfaces/IMixinLivingEntity.java index 5b12dcd..bf91a58 100644 --- a/src/main/java/cn/pupperclient/mixin/interfaces/IMixinLivingEntity.java +++ b/src/main/java/cn/pupperclient/mixin/interfaces/IMixinLivingEntity.java @@ -3,5 +3,5 @@ import net.minecraft.util.Hand; public interface IMixinLivingEntity { - void soarClient_CN$fakeSwingHand(Hand hand); + void pupper$fakeSwingHand(Hand hand); } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java index b16df75..b888cce 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java @@ -107,7 +107,7 @@ private void handleBlockBreaking(boolean breaking, CallbackInfo ci) { if (!this.world.getBlockState(blockPos).isAir()) { Direction direction = blockHitResult.getSide(); this.particleManager.addBlockBreakingParticles(blockPos, direction); - ((IMixinLivingEntity) player).soarClient_CN$fakeSwingHand(Hand.MAIN_HAND); + ((IMixinLivingEntity) player).pupper$fakeSwingHand(Hand.MAIN_HAND); } } } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/BufferRendererAccessor.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/BufferRendererAccessor.java deleted file mode 100644 index 1b90209..0000000 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/BufferRendererAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.pupperclient.mixin.mixins.minecraft.client.render; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import net.minecraft.client.gl.VertexBuffer; -import net.minecraft.client.render.BufferRenderer; - -@Mixin(BufferRenderer.class) -public interface BufferRendererAccessor { - @Accessor("currentVertexBuffer") - static void setCurrentVertexBuffer(VertexBuffer vertexBuffer) { - } -} diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinCamera.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinCamera.java index 29aef97..4aa215c 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinCamera.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinCamera.java @@ -34,11 +34,11 @@ public void lockRotation(BlockView focusedBlock, Entity cameraEntity, boolean is IMixinCameraEntity cameraOverriddenEntity = (IMixinCameraEntity) cameraEntity; if (firstTime && MinecraftClient.getInstance().player != null) { - cameraOverriddenEntity.soarClient_CN$setCameraPitch(client.player.getPitch()); - cameraOverriddenEntity.soarClient_CN$setCameraYaw(client.player.getYaw()); + cameraOverriddenEntity.pupper$setCameraPitch(client.player.getPitch()); + cameraOverriddenEntity.pupper$setCameraYaw(client.player.getYaw()); firstTime = false; } - this.setRotation(cameraOverriddenEntity.soarClient_CN$getCameraYaw(), cameraOverriddenEntity.soarClient_CN$getCameraPitch()); + this.setRotation(cameraOverriddenEntity.pupper$getCameraYaw(), cameraOverriddenEntity.pupper$getCameraPitch()); } if (FreelookMod.getInstance().isEnabled() && !FreelookMod.getInstance().isActive() && cameraEntity instanceof ClientPlayerEntity) { diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinEntity.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinEntity.java index 5d9d5c7..fb0ed5b 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinEntity.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinEntity.java @@ -84,25 +84,25 @@ public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo @Override @Unique - public float soarClient_CN$getCameraPitch() { + public float pupper$getCameraPitch() { return this.cameraPitch; } @Override @Unique - public float soarClient_CN$getCameraYaw() { + public float pupper$getCameraYaw() { return this.cameraYaw; } @Override @Unique - public void soarClient_CN$setCameraPitch(float pitch) { + public void pupper$setCameraPitch(float pitch) { this.cameraPitch = pitch; } @Override @Unique - public void soarClient_CN$setCameraYaw(float yaw) { + public void pupper$setCameraYaw(float yaw) { this.cameraYaw = yaw; } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinLivingEntity.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinLivingEntity.java index c41445b..46fd8b4 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinLivingEntity.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/entity/MixinLivingEntity.java @@ -80,7 +80,7 @@ private void onDamage(CallbackInfo info) { } @Override - public void soarClient_CN$fakeSwingHand(Hand hand) { + public void pupper$fakeSwingHand(Hand hand) { if (!this.handSwinging || this.handSwingTicks >= this.getHandSwingDuration() / 2 || this.handSwingTicks < 0) { this.handSwingTicks = -1; this.handSwinging = true; From 72cd44fdb0d701ff7ea4acd8b09826b0f9439867 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 14:48:25 +0800 Subject: [PATCH 08/21] fix(PupperInGameHud): aaaaaaaadd --- .../mixin/mixins/minecraft/client/render/PupperInGameHud.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/PupperInGameHud.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/PupperInGameHud.java index 2613860..57463d7 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/PupperInGameHud.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/PupperInGameHud.java @@ -55,7 +55,7 @@ private void drawCustomHotbar(DrawContext context, RenderTickCounter tickCounter int centerX = context.getScaledWindowWidth() / 2; int hotbarY = context.getScaledWindowHeight() - 22; - int selectedSlot = playerEntity.getInventory().selectedSlot; + int selectedSlot = playerEntity.getInventory().getSelectedSlot(); updateSlotAnimation(selectedSlot); updateOffhandAlpha(playerEntity); @@ -90,7 +90,7 @@ private void drawCustomHotbar(DrawContext context, RenderTickCounter tickCounter } this.renderHotbarItem(context, slotX, (int)(slotY + slotOffset), tickCounter, playerEntity, - playerEntity.getInventory().main.get(i), i + 1); + playerEntity.getInventory().getMainStacks().get(i), i + 1); } // 绘制副手物品(带透明度) From 7fbe132c11709c8c2834b84f3b7df46a11c848dd Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 15:16:22 +0800 Subject: [PATCH 09/21] chore(ExtendedRenderPipelineBuilder): Remove unnecessary ExtendedRenderPipelineBuilder --- .../shader/ExtendedRenderPipelineBuilder.java | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java diff --git a/src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java b/src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java deleted file mode 100644 index 49ab38c..0000000 --- a/src/main/java/cn/pupperclient/shader/ExtendedRenderPipelineBuilder.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.pupperclient.shader; - -import cn.pupperclient.mixin.interfaces.IRenderPipeline; -import com.mojang.blaze3d.pipeline.RenderPipeline; - -public class ExtendedRenderPipelineBuilder extends RenderPipeline.Builder { - private boolean lineSmooth; - - public ExtendedRenderPipelineBuilder(RenderPipeline.Snippet... snippets) { - for (RenderPipeline.Snippet snippet : snippets) { - withSnippet(snippet); - } - } - - public ExtendedRenderPipelineBuilder withLineSmooth() { - lineSmooth = true; - return this; - } - - @Override - public RenderPipeline build() { - RenderPipeline pipeline = super.build(); - ((IRenderPipeline) pipeline).pupper$setLineSmooth(lineSmooth); - - return pipeline; - } -} From 86b054a2554dd5b8cc840a5f693fa7be8432ab11 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 15:17:45 +0800 Subject: [PATCH 10/21] fix: Correct render item parameter in target method --- .../client/render/MixinHeldItemRenderer.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinHeldItemRenderer.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinHeldItemRenderer.java index d7ebc44..2352f65 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinHeldItemRenderer.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/render/MixinHeldItemRenderer.java @@ -26,22 +26,30 @@ public abstract class MixinHeldItemRenderer { @Shadow protected abstract void applySwingOffset(MatrixStack matrices, Arm arm, float swingProgress); - @Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = ("Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V"), ordinal = 1)) - private void renderFirstPersonItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, - float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, - VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) { + @Inject(method = "renderFirstPersonItem", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/render/item/HeldItemRenderer;" + + "renderItem(Lnet/minecraft/entity/LivingEntity;" + + "Lnet/minecraft/item/ItemStack;" + + "Lnet/minecraft/item/ItemDisplayContext;" + + "Lnet/minecraft/client/util/math/MatrixStack;" + + "Lnet/minecraft/client/render/VertexConsumerProvider;I)V", + ordinal = 1)) + private void onRenderFirstPersonItem(AbstractClientPlayerEntity player, float tickProgress, float pitch, + Hand hand, float swingProgress, ItemStack item, float equipProgress, + MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, + CallbackInfo ci) { + OldAnimationsMod mod = OldAnimationsMod.getInstance(); - OldAnimationsMod mod = OldAnimationsMod.getInstance(); + if (item.getItem() instanceof BowItem && mod.isEnabled() && mod.isOldBow()) { + matrices.translate(0f, 0.05f, 0.04f); + matrices.scale(0.93f, 1f, 1f); + } else if (item.getItem() instanceof FishingRodItem && mod.isEnabled() && mod.isOldRod()) { + matrices.translate(0.08f, -0.027f, -0.33f); + matrices.scale(0.93f, 1f, 1f); + } + } - if (item.getItem() instanceof BowItem && mod.isEnabled() && mod.isOldBow()) { - matrices.translate(0f, 0.05f, 0.04f); - matrices.scale(0.93f, 1f, 1f); - } else if (item.getItem() instanceof FishingRodItem && mod.isEnabled() && mod.isOldRod()) { - matrices.translate(0.08f, -0.027f, -0.33f); - matrices.scale(0.93f, 1f, 1f); - } - } - @Inject(method = "renderFirstPersonItem", at = @At("HEAD")) private void applyCustomHand(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, From 64274a41791ccfb506500c41212823b9560f0070 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 16:16:06 +0800 Subject: [PATCH 11/21] refactor: Update ImageHelper for Minecraft 1.21.5 compatibility --- .../management/auth/AuthManager.java | 266 ------- .../management/cape/CapeRenderer.java | 12 +- .../client/gui/MixinSplashScreen.java | 3 +- src/main/java/cn/pupperclient/skia/Skia.java | 48 +- .../pupperclient/skia/image/ImageHelper.java | 649 +++++++++++++++--- .../java/cn/pupperclient/utils/Unstable.java | 12 + 6 files changed, 590 insertions(+), 400 deletions(-) delete mode 100644 src/main/java/cn/pupperclient/management/auth/AuthManager.java create mode 100644 src/main/java/cn/pupperclient/utils/Unstable.java diff --git a/src/main/java/cn/pupperclient/management/auth/AuthManager.java b/src/main/java/cn/pupperclient/management/auth/AuthManager.java deleted file mode 100644 index 2fa18f5..0000000 --- a/src/main/java/cn/pupperclient/management/auth/AuthManager.java +++ /dev/null @@ -1,266 +0,0 @@ -package cn.pupperclient.management.auth; - -import cn.pupperclient.PupperClient; -import cn.pupperclient.utils.file.FileLocation; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; - -import java.io.File; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.security.MessageDigest; -import java.util.HashMap; -import java.util.Map; - -public class AuthManager { - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - private static final String SECRET_KEY = "pupper"; - private static AuthManager instance; - - private Map users = new HashMap<>(); - private UserData currentUser; - - public static AuthManager getInstance() { - if (instance == null) { - instance = new AuthManager(); - } - return instance; - } - - private AuthManager() { - loadUserData(); - } - - public static class UserData { - private String username; - private String passwordHash; - private long createdAt; - private long lastLogin; - - public UserData() {} - - public UserData(String username, String password) { - this.username = username; - this.passwordHash = hashPassword(password); - this.createdAt = System.currentTimeMillis(); - this.lastLogin = System.currentTimeMillis(); - } - - // Getters and Setters - public String getUsername() { return username; } - public void setUsername(String username) { this.username = username; } - public String getPasswordHash() { return passwordHash; } - public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; } - public long getCreatedAt() { return createdAt; } - public void setCreatedAt(long createdAt) { this.createdAt = createdAt; } - public long getLastLogin() { return lastLogin; } - public void setLastLogin(long lastLogin) { this.lastLogin = lastLogin; } - } - - private static String hashPassword(String password) { - try { - String data = password + SECRET_KEY; - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8)); - - // 转换为十六进制字符串 - StringBuilder hexString = new StringBuilder(); - for (byte b : hash) { - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) hexString.append('0'); - hexString.append(hex); - } - return hexString.toString(); - } catch (Exception e) { - PupperClient.LOGGER.error("Failed to hash password", e); - throw new RuntimeException("Password hashing failed", e); - } - } - - /** - * 加载用户数据 - */ - private void loadUserData() { - try { - File userDataFile = FileLocation.USER_DATA_DIR; - Path userDataPath = userDataFile.toPath(); - - PupperClient.LOGGER.info("Attempting to load user data from: {}", userDataPath); - - // 如果文件不存在,创建空的数据文件 - if (!userDataFile.exists()) { - PupperClient.LOGGER.info("No existing user data found, creating new file"); - users = new HashMap<>(); - - // 确保目录存在 - File parentDir = userDataFile.getParentFile(); - if (parentDir != null && !parentDir.exists()) { - boolean dirsCreated = parentDir.mkdirs(); - if (!dirsCreated) { - PupperClient.LOGGER.warn("Failed to create parent directories: {}", parentDir.getAbsolutePath()); - } - } - - // 创建空文件 - boolean fileCreated = userDataFile.createNewFile(); - if (!fileCreated) { - PupperClient.LOGGER.warn("Failed to create user data file: {}", userDataPath); - } - - saveUserData(); // 保存空的用户数据 - return; - } - - // 检查是否是文件而不是目录 - if (userDataFile.isDirectory()) { - PupperClient.LOGGER.error("User data path is a directory, not a file: {}", userDataPath); - users = new HashMap<>(); - return; - } - - // 读取并解析用户数据 - String json = new String(Files.readAllBytes(userDataPath)); - if (json.trim().isEmpty()) { - users = new HashMap<>(); - PupperClient.LOGGER.info("User data file is empty, starting fresh"); - } else { - java.lang.reflect.Type type = new TypeToken>(){}.getType(); - users = GSON.fromJson(json, type); - - if (users == null) { - users = new HashMap<>(); - } - - PupperClient.LOGGER.info("Loaded {} users from disk", users.size()); - } - } catch (Exception e) { - PupperClient.LOGGER.error("Failed to load user data from: {}", FileLocation.USER_DATA_DIR.getAbsolutePath(), e); - users = new HashMap<>(); - } - } - - private void saveUserData() { - try { - File userDataFile = FileLocation.USER_DATA_DIR; - Path userDataPath = userDataFile.toPath(); - - PupperClient.LOGGER.info("Attempting to save user data to: {}", userDataPath); - - File parentDir = userDataFile.getParentFile(); - if (parentDir != null && !parentDir.exists()) { - boolean dirsCreated = parentDir.mkdirs(); - if (!dirsCreated) { - PupperClient.LOGGER.error("Failed to create parent directories: {}", parentDir.getAbsolutePath()); - return; - } - } - - if (userDataFile.isDirectory()) { - PupperClient.LOGGER.error("Cannot save user data: path is a directory: {}", userDataPath); - return; - } - - String json = GSON.toJson(users); - Files.write(userDataPath, json.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); - - PupperClient.LOGGER.info("Successfully saved {} users to disk", users.size()); - } catch (Exception e) { - PupperClient.LOGGER.error("Failed to save user data to: {}", FileLocation.USER_DATA_DIR.getAbsolutePath(), e); - } - } - - public boolean register(String username, String password) { - if (username == null || username.trim().isEmpty() || password == null || password.isEmpty()) { - PupperClient.LOGGER.warn("Registration failed: invalid username or password"); - return false; - } - - username = username.trim(); - - if (users.containsKey(username)) { - PupperClient.LOGGER.warn("Registration failed: user already exists: {}", username); - return false; - } - - UserData userData = new UserData(username, password); - users.put(username, userData); - saveUserData(); - - PupperClient.LOGGER.info("Registered new user: {}", username); - return true; - } - - public boolean login(String username, String password) { - if (username == null || password == null) { - return false; - } - - username = username.trim(); - UserData userData = users.get(username); - - if (userData == null) { - PupperClient.LOGGER.warn("Login failed: user not found: {}", username); - return false; - } - - String inputHash = hashPassword(password); - if (userData.getPasswordHash().equals(inputHash)) { - userData.setLastLogin(System.currentTimeMillis()); - currentUser = userData; - saveUserData(); - - PupperClient.LOGGER.info("User logged in: {}", username); - return true; - } - - PupperClient.LOGGER.warn("Login failed: incorrect password for user: {}", username); - return false; // 密码错误 - } - - public void logout() { - if (currentUser != null) { - PupperClient.LOGGER.info("User logged out: {}", currentUser.getUsername()); - } - currentUser = null; - } - - public boolean isLoggedIn() { - return currentUser != null; - } - - public UserData getCurrentUser() { - return currentUser; - } - - public boolean userExists(String username) { - return users.containsKey(username.trim()); - } - - public boolean deleteUser(String username) { - if (username == null) { - return false; - } - - username = username.trim(); - if (users.remove(username) != null) { - if (currentUser != null && currentUser.getUsername().equals(username)) { - logout(); - } - saveUserData(); - PupperClient.LOGGER.info("Deleted user: {}", username); - return true; - } - return false; - } - - public int getUserCount() { - return users.size(); - } - - public String getUserDataPath() { - return FileLocation.USER_DATA_DIR.getAbsolutePath(); - } -} diff --git a/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java b/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java index 1fc227d..2a9dd98 100644 --- a/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java +++ b/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java @@ -9,13 +9,15 @@ import io.github.humbleui.types.RRect; import io.github.humbleui.types.Rect; +import java.util.Objects; + public class CapeRenderer { public static void renderCapePreview(Identifier capeTexture, float x, float y, float width, float height) { if (capeTexture == null) return; try { - int textureId = MinecraftClient.getInstance().getTextureManager().getTexture(capeTexture).getGlId(); + var textureId = MinecraftClient.getInstance().getTextureManager().getTexture(capeTexture).getGlTexture(); // 尝试不同的纹理尺寸 boolean loaded = Skia.getImageHelper().load(textureId, 64, 32, SurfaceOrigin.TOP_LEFT) || @@ -28,7 +30,7 @@ public static void renderCapePreview(Identifier capeTexture, float x, float y, f Rect srcRect = Rect.makeXYWH(1, 1, 10, 16); Rect dstRect = Rect.makeXYWH(0, 0, 10, 16); - Skia.getCanvas().drawImageRect(Skia.getImageHelper().get(textureId), srcRect, dstRect, null, false); + Skia.getCanvas().drawImageRect(Objects.requireNonNull(Skia.getImageHelper().get(textureId)), srcRect, dstRect, null, false); Skia.restore(); @@ -38,7 +40,7 @@ public static void renderCapePreview(Identifier capeTexture, float x, float y, f Rect srcRect2 = Rect.makeXYWH(12, 1, 10, 16); Rect dstRect2 = Rect.makeXYWH(0, 0, 10, 16); - Skia.getCanvas().drawImageRect(Skia.getImageHelper().get(textureId), srcRect2, dstRect2, null, false); + Skia.getCanvas().drawImageRect(Objects.requireNonNull(Skia.getImageHelper().get(textureId)), srcRect2, dstRect2, null, false); Skia.restore(); } @@ -52,7 +54,7 @@ public static void renderRoundedCapePreview(Identifier capeTexture, float x, flo if (capeTexture == null) return; try { - int textureId = MinecraftClient.getInstance().getTextureManager().getTexture(capeTexture).getGlId(); + var textureId = MinecraftClient.getInstance().getTextureManager().getTexture(capeTexture).getGlTexture(); // 尝试不同的纹理尺寸 boolean loaded = Skia.getImageHelper().load(textureId, 64, 32, SurfaceOrigin.TOP_LEFT) || @@ -67,7 +69,7 @@ public static void renderRoundedCapePreview(Identifier capeTexture, float x, flo Skia.save(); Skia.getCanvas().clipPath(path, ClipMode.INTERSECT, true); - Skia.getCanvas().drawImageRect(Skia.getImageHelper().get(textureId), srcRect, dstRect, null, false); + Skia.getCanvas().drawImageRect(Objects.requireNonNull(Skia.getImageHelper().get(textureId)), srcRect, dstRect, null, false); Skia.restore(); } } catch (Exception e) { diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java index 534c732..c03af05 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java @@ -3,6 +3,7 @@ import cn.pupperclient.skia.Skia; import cn.pupperclient.skia.context.SkiaContext; import cn.pupperclient.skia.font.Fonts; +import com.mojang.blaze3d.textures.GpuTexture; import io.github.humbleui.skija.Canvas; import io.github.humbleui.skija.Font; import io.github.humbleui.types.Rect; @@ -248,7 +249,7 @@ private void renderLoadingScreen(int width, int height, long timePassed, float a @Unique private void drawLogo(int x, int y, int size, float alpha) { // Draw logo using Skia - int textureId = MinecraftClient.getInstance().getTextureManager().getTexture(CUSTOM_LOGO).getGlId(); + GpuTexture textureId = MinecraftClient.getInstance().getTextureManager().getTexture(CUSTOM_LOGO).getGlTexture(); Skia.drawImage(textureId, x, y, size, size, alpha); } diff --git a/src/main/java/cn/pupperclient/skia/Skia.java b/src/main/java/cn/pupperclient/skia/Skia.java index 223c2f3..24295e8 100644 --- a/src/main/java/cn/pupperclient/skia/Skia.java +++ b/src/main/java/cn/pupperclient/skia/Skia.java @@ -2,11 +2,13 @@ import java.awt.Color; import java.io.File; +import java.util.Objects; import cn.pupperclient.management.mod.impl.settings.HUDModSettings; import cn.pupperclient.shader.impl.Kawaseblur; import cn.pupperclient.skia.context.SkiaContext; import cn.pupperclient.skia.image.ImageHelper; +import com.mojang.blaze3d.textures.GpuTexture; import io.github.humbleui.skija.Canvas; import io.github.humbleui.skija.ClipMode; import io.github.humbleui.skija.FilterTileMode; @@ -119,49 +121,49 @@ public static void drawImage(String path, float x, float y, float width, float h path = "/assets/pupper/" + path; - if (imageHelper.load(path)) { + if (imageHelper.load(path) != null) { getCanvas().drawImageRect(imageHelper.get(path), Rect.makeXYWH(x, y, width, height)); } } - public static void drawImage(int textureId, float x, float y, float width, float height, float alpha, + public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height, float alpha, SurfaceOrigin origin) { - if (imageHelper.load(textureId, width, height, origin)) { + if (imageHelper.load(Gputexture, width, height) != null) { Paint paint = new Paint(); paint.setAlpha((int) (255 * alpha)); - getCanvas().drawImageRect(imageHelper.get(textureId), Rect.makeXYWH(x, y, width, height), paint); + getCanvas().drawImageRect(imageHelper.get(Gputexture), Rect.makeXYWH(x, y, width, height), paint); } } - public static void drawImage(int textureId, float x, float y, float width, float height, float alpha) { - drawImage(textureId, x, y, width, height, alpha, SurfaceOrigin.TOP_LEFT); + public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height, float alpha) { + drawImage(Gputexture, x, y, width, height, alpha, SurfaceOrigin.TOP_LEFT); } public static void drawImage(File file, float x, float y, float width, float height) { - if (imageHelper.load(file)) { - getCanvas().drawImageRect(imageHelper.get(file.getName()), Rect.makeXYWH(x, y, width, height)); + if (imageHelper.load(file) != null) { + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), Rect.makeXYWH(x, y, width, height)); } } - public static void drawImage(int textureId, float x, float y, float width, float height, SurfaceOrigin origin) { + public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height, SurfaceOrigin origin) { - if (imageHelper.load(textureId, width, height, origin)) { - getCanvas().drawImageRect(imageHelper.get(textureId), Rect.makeXYWH(x, y, width, height)); + if (imageHelper.load(Gputexture, width, height) != null) { + getCanvas().drawImageRect(imageHelper.get(Gputexture), Rect.makeXYWH(x, y, width, height)); } } - public static void drawImage(int textureId, float x, float y, float width, float height) { - drawImage(textureId, x, y, width, height, SurfaceOrigin.TOP_LEFT); + public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height) { + drawImage(Gputexture, x, y, width, height, SurfaceOrigin.TOP_LEFT); } - public static void drawRoundedImage(int textureId, float x, float y, float width, float height, float radius) { + public static void drawRoundedImage(GpuTexture Gputexture, float x, float y, float width, float height, float radius) { Path path = Path.makeRRect(RRect.makeXYWH(x, y, width, height, radius)); save(); getCanvas().clipPath(path, ClipMode.INTERSECT, true); - drawImage(textureId, x, y, width, height); + drawImage(Gputexture, x, y, width, height); restore(); } @@ -185,23 +187,23 @@ public static void drawRoundedImage(File file, float x, float y, float width, fl restore(); } - public static void drawRoundedImage(int textureId, float x, float y, float width, float height, float radius, + public static void drawRoundedImage(GpuTexture Gputexture, float x, float y, float width, float height, float radius, float alpha, SurfaceOrigin origin) { Path path = Path.makeRRect(RRect.makeXYWH(x, y, width, height, radius)); save(); getCanvas().clipPath(path, ClipMode.INTERSECT, true); - drawImage(textureId, x, y, width, height, alpha, origin); + drawImage(Gputexture, x, y, width, height, alpha, origin); restore(); } - public static void drawRoundedImage(int textureId, float x, float y, float width, float height, float radius, + public static void drawRoundedImage(GpuTexture Gputexture, float x, float y, float width, float height, float radius, float alpha) { - drawRoundedImage(textureId, x, y, width, height, radius, alpha, SurfaceOrigin.TOP_LEFT); + drawRoundedImage(Gputexture, x, y, width, height, radius, alpha, SurfaceOrigin.TOP_LEFT); } public static void drawPlayerHead(File file, float x, float y, float width, float height, float radius) { - if (imageHelper.load(file)) { + if (imageHelper.load(file) != null) { Path path = Path.makeRRect(RRect.makeXYWH(x, y, width, height, radius)); @@ -218,7 +220,7 @@ public static void drawPlayerHead(File file, float x, float y, float width, floa } public static void drawSkin(File file, float x, float y, float scale) { - if (imageHelper.load(file)) { + if (imageHelper.load(file) != null) { Rect head = Rect.makeXYWH(8, 8, 8, 8); Rect headLayer = Rect.makeXYWH(40, 8, 8, 8); @@ -282,8 +284,8 @@ public static void drawSkin(File file, float x, float y, float scale) { public static void drawMinecraftImage(String path, float x, float y, float width, float height) { Identifier identifier = Identifier.of("minecraft", path); - if (imageHelper.load(identifier)) { - getCanvas().drawImageRect(imageHelper.get(identifier.getPath()), Rect.makeXYWH(x, y, width, height)); + if (imageHelper.load(identifier) != null) { + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(identifier.getPath())), Rect.makeXYWH(x, y, width, height)); } } diff --git a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java index 3ea6054..1802296 100644 --- a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java +++ b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java @@ -1,116 +1,555 @@ package cn.pupperclient.skia.image; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import org.lwjgl.opengl.GL11; - -import cn.pupperclient.skia.context.SkiaContext; +import cn.pupperclient.PupperLogger; import cn.pupperclient.skia.utils.SkiaUtils; - -import io.github.humbleui.skija.ColorType; -import io.github.humbleui.skija.Image; -import io.github.humbleui.skija.SurfaceOrigin; +import cn.pupperclient.utils.Unstable; +import com.mojang.blaze3d.buffers.BufferType; +import com.mojang.blaze3d.buffers.BufferUsage; +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.GpuTexture; +import io.github.humbleui.skija.*; import net.minecraft.client.MinecraftClient; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Blocking; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.BufferUtils; + +import java.io.*; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; public class ImageHelper { + private static final ExecutorService LOADER_EXECUTOR = Executors.newFixedThreadPool(2); + + // 缓存系统 + private final Map fileImages = new ConcurrentHashMap<>(); + private final Map resourceImages = new ConcurrentHashMap<>(); + private final Map gpuTextureImages = new ConcurrentHashMap<>(); + private final Map> loadingFutures = new ConcurrentHashMap<>(); + + // 回调系统 + private final Map> callbacks = new ConcurrentHashMap<>(); + + /** + * 从 GpuTexture 加载图像 + */ + public CompletableFuture loadAsync(GpuTexture texture) { + if (texture == null) { + return CompletableFuture.completedFuture(null); + } + + // 检查缓存 + if (gpuTextureImages.containsKey(texture)) { + return CompletableFuture.completedFuture(gpuTextureImages.get(texture)); + } + + String key = "gpu:" + texture.hashCode(); + if (loadingFutures.containsKey(key)) { + return loadingFutures.get(key); + } + + CompletableFuture future = CompletableFuture.supplyAsync(() -> { + try { + // 读取 GpuTexture 数据 + ByteBuffer pixelData = readGpuTextureData(texture); + if (pixelData == null) { + return null; + } + + // 创建 Skia Image + ImageInfo info = ImageInfo.makeS32( + texture.getWidth(0), + texture.getHeight(0), + ColorAlphaType.UNPREMUL + ); + + Data data = Data.makeFromBytes(pixelData.array()); + Image image = Image.makeRasterFromData(info, data, info.getMinRowBytes()); + + // 缓存结果 + gpuTextureImages.put(texture, image); + return image; + + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + }, LOADER_EXECUTOR); + + loadingFutures.put(key, future); + future.whenComplete((image, error) -> loadingFutures.remove(key)); + + return future; + } + + /** + * 同步从 GpuTexture 加载图像 + */ + @Nullable + @Unstable(reason = "float will be coerced to int") + public Image load(GpuTexture texture, float width, float height) { + if (texture == null) { + return null; + } + + // 检查缓存 + if (gpuTextureImages.containsKey(texture)) { + return gpuTextureImages.get(texture); + } + + try { + // 必须在渲染线程执行 + RenderSystem.assertOnRenderThread(); + + // 读取 GpuTexture 数据 + ByteBuffer pixelData = readGpuTextureData(texture); + if (pixelData == null) { + return null; + } + + // 创建 Skia Image + ImageInfo info = ImageInfo.makeS32( + (int) width, + (int) height, + ColorAlphaType.UNPREMUL + ); + + Data data = Data.makeFromBytes(pixelData.array()); + Image image = Image.makeRasterFromData(info, data, info.getMinRowBytes()); + + // 缓存结果 + gpuTextureImages.put(texture, image); + return image; + + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + } + + /** + * 从文件路径异步加载图像 + */ + public CompletableFuture loadAsync(String filePath) { + if (filePath == null || filePath.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + + // 检查缓存 + if (fileImages.containsKey(filePath)) { + return CompletableFuture.completedFuture(fileImages.get(filePath)); + } + + if (loadingFutures.containsKey(filePath)) { + return loadingFutures.get(filePath); + } + + CompletableFuture future = CompletableFuture.supplyAsync(() -> { + try { + Optional encodedBytes = SkiaUtils.convertToBytes(filePath); + if (encodedBytes.isEmpty()) { + return null; + } + + Image image = Image.makeDeferredFromEncodedBytes(encodedBytes.get()); + fileImages.put(filePath, image); + + return image; + + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + }, LOADER_EXECUTOR); + + loadingFutures.put(filePath, future); + future.whenComplete((image, error) -> { + loadingFutures.remove(filePath); + if (image != null && callbacks.containsKey(filePath)) { + callbacks.get(filePath).accept(image); + callbacks.remove(filePath); + } + }); + + return future; + } + + /** + * 从文件异步加载图像 + */ + public CompletableFuture loadAsync(File file) { + if (file == null || !file.exists()) { + return CompletableFuture.completedFuture(null); + } + + String filePath = file.getAbsolutePath(); + return loadAsync(filePath); + } + + /** + * 从资源标识符异步加载图像 + */ + public CompletableFuture loadAsync(Identifier identifier) { + if (identifier == null) { + return CompletableFuture.completedFuture(null); + } + + // 检查缓存 + if (resourceImages.containsKey(identifier)) { + return CompletableFuture.completedFuture(resourceImages.get(identifier)); + } + + String key = "resource:" + identifier.toString(); + if (loadingFutures.containsKey(key)) { + return loadingFutures.get(key); + } + + CompletableFuture future = CompletableFuture.supplyAsync(() -> { + try { + ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager(); + Resource resource = resourceManager.getResource(identifier).orElse(null); + if (resource == null) { + return null; + } + + try (InputStream inputStream = resource.getInputStream()) { + byte[] imageData = inputStream.readAllBytes(); + Image image = Image.makeDeferredFromEncodedBytes(imageData); + + resourceImages.put(identifier, image); + + return image; + } + + } catch (IOException e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + }, LOADER_EXECUTOR); + + loadingFutures.put(key, future); + future.whenComplete((image, error) -> loadingFutures.remove(key)); + + return future; + } + + /** + * 同步加载文件 + */ + @Nullable + public Image load(String filePath) { + try { + Optional encodedBytes = SkiaUtils.convertToBytes(filePath); + if (encodedBytes.isEmpty()) { + return null; + } + + Image image = Image.makeDeferredFromEncodedBytes(encodedBytes.get()); + fileImages.put(filePath, image); + + return image; + + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + } + + /** + * 同步加载文件 + */ + @Nullable + public Image load(File file) { + if (file == null || !file.exists()) { + return null; + } + + try (FileInputStream fis = new FileInputStream(file)) { + byte[] encoded = org.apache.commons.io.IOUtils.toByteArray(fis); + Image image = Image.makeDeferredFromEncodedBytes(encoded); + + fileImages.put(file.getAbsolutePath(), image); + + return image; + + } catch (IOException e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + } + + /** + * 同步加载资源 + */ + @Nullable + public Image load(Identifier identifier) { + if (identifier == null) { + return null; + } + + // 检查缓存 + if (resourceImages.containsKey(identifier)) { + return resourceImages.get(identifier); + } + + try { + ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager(); + Resource resource = resourceManager.getResource(identifier).orElse(null); + if (resource == null) { + return null; + } + + try (InputStream inputStream = resource.getInputStream()) { + byte[] imageData = inputStream.readAllBytes(); + Image image = Image.makeDeferredFromEncodedBytes(imageData); + + resourceImages.put(identifier, image); + + return image; + } + + } catch (IOException e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + } + + /** + * 添加加载完成回调 + */ + public void addCallback(String key, Consumer callback) { + callbacks.put(key, callback); + } + + /** + * 获取缓存的图像 + */ + @Nullable + public Image get(String key) { + return fileImages.get(key); + } + + @Nullable + public Image get(Identifier identifier) { + return resourceImages.get(identifier); + } + + @Nullable + public Image get(GpuTexture texture) { + return gpuTextureImages.get(texture); + } + + public static ByteBuffer readGpuTextureData(GpuTexture texture) { + if (texture == null) { + return null; + } + + RenderSystem.assertOnRenderThread(); + + try { + int width = texture.getWidth(0); + int height = texture.getHeight(0); + int bufferSize = width * height * 4; // RGBA8 + + // 创建用于存储读取结果的引用 + AtomicReference result = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + + // 创建 GPU 缓冲区用于读取 + GpuBuffer readBuffer = RenderSystem.getDevice().createBuffer( + () -> "TextureReadBuffer", + BufferType.PIXEL_PACK, + BufferUsage.STREAM_READ, + bufferSize + ); + + try { + // 创建命令编码器 + var commandEncoder = RenderSystem.getDevice().createCommandEncoder(); + + // 复制纹理数据到缓冲区,并设置回调 + commandEncoder.copyTextureToBuffer( + texture, + readBuffer, + 0, + () -> { + // 数据已上传到缓冲区的回调 + try { + // 在回调中读取缓冲区数据 + try (GpuBuffer.ReadView readView = commandEncoder.readBuffer(readBuffer)) { + ByteBuffer pixelData = BufferUtils.createByteBuffer(bufferSize); + ByteBuffer sourceBuffer = readView.data(); + sourceBuffer.rewind(); + pixelData.put(sourceBuffer); + pixelData.flip(); + + result.set(pixelData); + } + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + } finally { + latch.countDown(); // 通知主线程数据已读取完成 + } + }, + 0 // mipLevel + ); + + // 等待数据读取完成 + boolean success = latch.await(5, TimeUnit.SECONDS); + if (!success) { + System.err.println("读取纹理数据超时"); + return null; + } + + return result.get(); + + } finally { + readBuffer.close(); + } + + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + } + + @Nullable + @Deprecated + public GpuTexture convertToGpuTexture(Image image) { + if (image == null) { + return null; + } + + try { + RenderSystem.assertOnRenderThread(); + + int width = image.getWidth(); + int height = image.getHeight(); + + // 获取图像数据 + ImageInfo info = image.getImageInfo(); + Data data = image.encodeToData(EncodedImageFormat.PNG); + if (data == null) { + return null; + } + + // 创建 GpuTexture + GpuTexture texture = RenderSystem.getDevice().createTexture( + () -> "SkiaImageTexture", + com.mojang.blaze3d.textures.TextureFormat.RGBA8, + width, + height, + 1 + ); + + return texture; + + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + } + + /** + * 清理缓存 + */ + public void clearCache() { + // 关闭所有缓存的图像 + for (Image image : fileImages.values()) { + image.close(); + } + for (Image image : resourceImages.values()) { + image.close(); + } + for (Image image : gpuTextureImages.values()) { + image.close(); + } + + fileImages.clear(); + resourceImages.clear(); + gpuTextureImages.clear(); + loadingFutures.clear(); + callbacks.clear(); + } + + /** + * 清理特定类型的缓存 + */ + public void clearFileCache() { + fileImages.values().forEach(Image::close); + fileImages.clear(); + } + + public void clearResourceCache() { + resourceImages.values().forEach(Image::close); + resourceImages.clear(); + } + + public void clearGpuTextureCache() { + gpuTextureImages.values().forEach(Image::close); + gpuTextureImages.clear(); + } + + /** + * 检查图像是否已缓存 + */ + public boolean isCached(String key) { + return fileImages.containsKey(key); + } + + public boolean isCached(Identifier identifier) { + return resourceImages.containsKey(identifier); + } + + public boolean isCached(GpuTexture texture) { + return gpuTextureImages.containsKey(texture); + } + + /** + * 获取缓存统计信息 + */ + public CacheStats getCacheStats() { + return new CacheStats( + fileImages.size(), + resourceImages.size(), + gpuTextureImages.size(), + loadingFutures.size() + ); + } + + /** + * 预加载常用资源 + */ + public void preloadCommonResources() { + + } + + public void close() { + clearCache(); + LOADER_EXECUTOR.shutdown(); + } + + public record CacheStats(int fileImages, int resourceImages, int gpuTextureImages, int loadingTasks) { + + @Override + public String toString() { + return String.format( + "CacheStats{文件图像=%d, 资源图像=%d, GPU纹理=%d, 加载任务=%d}", + fileImages, resourceImages, gpuTextureImages, loadingTasks + ); + } + } + + private static final ImageHelper INSTANCE = new ImageHelper(); - private Map images = new HashMap<>(); - private Map textures = new HashMap<>(); - - public boolean load(int texture, float width, float height, SurfaceOrigin origin) { - - if (!textures.containsKey(texture)) { - Image image = Image.adoptGLTextureFrom(SkiaContext.getContext(), texture, GL11.GL_TEXTURE_2D, (int) width, - (int) height, GL11.GL_RGBA8, origin, ColorType.RGBA_8888); - textures.put(texture, image); - } - - return true; - } - - public boolean load(Identifier identifier) { - - if (!images.containsKey(identifier.getPath())) { - ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager(); - Resource resource; - try { - resource = resourceManager.getResourceOrThrow(identifier); - try (InputStream inputStream = resource.getInputStream()) { - - byte[] imageData = inputStream.readAllBytes(); - Image image = Image.makeDeferredFromEncodedBytes(imageData); - if (image == null) { - return false; - } - images.put(identifier.getPath(), image); - return true; - } catch (IOException e) { - e.printStackTrace(); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - - - } - return true; - } - - public boolean load(String filePath) { - if (!images.containsKey(filePath)) { - Optional encodedBytes = SkiaUtils.convertToBytes(filePath); - if (encodedBytes.isPresent()) { - images.put(filePath, Image.makeDeferredFromEncodedBytes(encodedBytes.get())); - return true; - } else { - return false; - } - } - return true; - } - - public boolean load(File file) { - - if (!images.containsKey(file.getName())) { - - try { - byte[] encoded = org.apache.commons.io.IOUtils.toByteArray(new FileInputStream(file)); - images.put(file.getName(), Image.makeDeferredFromEncodedBytes(encoded)); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - return true; - } - - public Image get(String path) { - - if (images.containsKey(path)) { - return images.get(path); - } - - return null; - } - - public Image get(int texture) { - - if (textures.containsKey(texture)) { - return textures.get(texture); - } - - return null; - } + public static ImageHelper getInstance() { + return INSTANCE; + } } diff --git a/src/main/java/cn/pupperclient/utils/Unstable.java b/src/main/java/cn/pupperclient/utils/Unstable.java new file mode 100644 index 0000000..ec1e67f --- /dev/null +++ b/src/main/java/cn/pupperclient/utils/Unstable.java @@ -0,0 +1,12 @@ +package cn.pupperclient.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Unstable { + String reason() default ""; +} From 0632a218ef463ed69b5372ccd2b71ecf1b17f79d Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 17:18:53 +0800 Subject: [PATCH 12/21] refactor: Update Skia for Minecraft 1.21.5 --- src/main/java/cn/pupperclient/skia/Skia.java | 36 +- .../skia/context/SkiaContext.java | 594 +++++++++++++++--- .../pupperclient/skia/image/ImageHelper.java | 74 ++- 3 files changed, 602 insertions(+), 102 deletions(-) diff --git a/src/main/java/cn/pupperclient/skia/Skia.java b/src/main/java/cn/pupperclient/skia/Skia.java index 24295e8..dd9eda3 100644 --- a/src/main/java/cn/pupperclient/skia/Skia.java +++ b/src/main/java/cn/pupperclient/skia/Skia.java @@ -122,7 +122,7 @@ public static void drawImage(String path, float x, float y, float width, float h path = "/assets/pupper/" + path; if (imageHelper.load(path) != null) { - getCanvas().drawImageRect(imageHelper.get(path), Rect.makeXYWH(x, y, width, height)); + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(path)), Rect.makeXYWH(x, y, width, height)); } } @@ -132,7 +132,7 @@ public static void drawImage(GpuTexture Gputexture, float x, float y, float widt if (imageHelper.load(Gputexture, width, height) != null) { Paint paint = new Paint(); paint.setAlpha((int) (255 * alpha)); - getCanvas().drawImageRect(imageHelper.get(Gputexture), Rect.makeXYWH(x, y, width, height), paint); + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(Gputexture)), Rect.makeXYWH(x, y, width, height), paint); } } @@ -142,14 +142,14 @@ public static void drawImage(GpuTexture Gputexture, float x, float y, float widt public static void drawImage(File file, float x, float y, float width, float height) { if (imageHelper.load(file) != null) { - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), Rect.makeXYWH(x, y, width, height)); + getCanvas().drawImageRect(Objects.requireNonNull(Objects.requireNonNull(imageHelper.get(file.getName()))), Rect.makeXYWH(x, y, width, height)); } } public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height, SurfaceOrigin origin) { if (imageHelper.load(Gputexture, width, height) != null) { - getCanvas().drawImageRect(imageHelper.get(Gputexture), Rect.makeXYWH(x, y, width, height)); + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(Gputexture)), Rect.makeXYWH(x, y, width, height)); } } @@ -213,8 +213,8 @@ public static void drawPlayerHead(File file, float x, float y, float width, floa save(); getCanvas().clipPath(path, ClipMode.INTERSECT, true); - getCanvas().drawImageRect(imageHelper.get(file.getName()), srcRect, dstRect, null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), srcRect1, dstRect, null, false); + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), srcRect, dstRect, null, false); + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), srcRect1, dstRect, null, false); restore(); } } @@ -237,42 +237,42 @@ public static void drawSkin(File file, float x, float y, float scale) { save(); scale(x, y, scale); - getCanvas().drawImageRect(imageHelper.get(file.getName()), head, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), head, Rect.makeXYWH(x + leftArm.getWidth(), y, head.getWidth(), head.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), headLayer, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), headLayer, Rect.makeXYWH(x + leftArm.getWidth(), y, headLayer.getWidth(), headLayer.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), body, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), body, Rect.makeXYWH(x + leftArm.getWidth(), y + head.getHeight(), body.getWidth(), body.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), bodyLayer, Rect.makeXYWH(x + leftArm.getWidth(), + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), bodyLayer, Rect.makeXYWH(x + leftArm.getWidth(), y + headLayer.getHeight(), bodyLayer.getWidth(), bodyLayer.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), leftArm, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), leftArm, Rect.makeXYWH(x, y + head.getHeight(), leftArm.getWidth(), leftArm.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), leftArmLayer, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), leftArmLayer, Rect.makeXYWH(x, y + headLayer.getHeight(), leftArmLayer.getWidth(), leftArmLayer.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), rightArm, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), rightArm, Rect.makeXYWH(x + leftArm.getWidth() + body.getWidth(), y + head.getHeight(), rightArm.getWidth(), rightArm.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), rightArmLayer, + getCanvas().drawImageRect(Objects.requireNonNull(Objects.requireNonNull(imageHelper.get(file.getName()))), rightArmLayer, Rect.makeXYWH(x + leftArmLayer.getWidth() + bodyLayer.getWidth(), y + headLayer.getHeight(), rightArmLayer.getWidth(), rightArmLayer.getHeight()), null, false); getCanvas().drawImageRect( - imageHelper.get(file.getName()), leftLeg, Rect.makeXYWH(x + leftArm.getWidth(), + Objects.requireNonNull(imageHelper.get(file.getName())), leftLeg, Rect.makeXYWH(x + leftArm.getWidth(), y + head.getHeight() + body.getHeight(), leftLeg.getWidth(), leftLeg.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), leftLegLayer, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), leftLegLayer, Rect.makeXYWH(x + leftArmLayer.getWidth(), y + headLayer.getHeight() + bodyLayer.getHeight(), leftLegLayer.getWidth(), leftLegLayer.getHeight()), null, false); getCanvas() - .drawImageRect(imageHelper.get(file.getName()), rightLeg, + .drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), rightLeg, Rect.makeXYWH(x + leftArm.getWidth() + leftLeg.getWidth(), y + head.getHeight() + body.getHeight(), rightLeg.getWidth(), rightLeg.getHeight()), null, false); - getCanvas().drawImageRect(imageHelper.get(file.getName()), rightLegLayer, + getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), rightLegLayer, Rect.makeXYWH(x + leftArmLayer.getWidth() + leftLegLayer.getWidth(), y + headLayer.getHeight() + bodyLayer.getHeight(), rightLegLayer.getWidth(), rightLegLayer.getHeight()), diff --git a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java index 2000124..269b4ff 100644 --- a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java +++ b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java @@ -1,92 +1,520 @@ package cn.pupperclient.skia.context; +import cn.pupperclient.PupperLogger; +import cn.pupperclient.skia.image.ImageHelper; +import cn.pupperclient.utils.Unstable; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.GpuTexture; +import io.github.humbleui.skija.*; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.texture.NativeImage; +import org.jetbrains.annotations.Nullable; + +import java.nio.ByteBuffer; import java.util.function.Consumer; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL13; -import org.lwjgl.opengl.GL33; +@Environment(EnvType.CLIENT) +public class SkiaContext { -import com.mojang.blaze3d.opengl.GlConst; -import com.mojang.blaze3d.systems.RenderSystem; + private static DirectContext directContext = null; + private static Surface skiaSurface = null; + private static BackendRenderTarget renderTarget = null; -import io.github.humbleui.skija.BackendRenderTarget; -import io.github.humbleui.skija.Canvas; -import io.github.humbleui.skija.ColorSpace; -import io.github.humbleui.skija.DirectContext; -import io.github.humbleui.skija.Surface; -import io.github.humbleui.skija.SurfaceColorFormat; -import io.github.humbleui.skija.SurfaceOrigin; -import net.minecraft.client.MinecraftClient; + private static GpuTexture currentTexture = null; + private static NativeImage currentNativeImage = null; -public class SkiaContext { + private static int surfaceWidth = 0; + private static int surfaceHeight = 0; + + /** + * 使用 GpuTexture 创建 Skia 表面 + */ + public static void createSurfaceFromGpuTexture(GpuTexture texture) { + if (texture == null) { + throw new IllegalArgumentException("GpuTexture cannot be null"); + } + + // 清理旧资源 + cleanup(); + + // 保存当前纹理 + currentTexture = texture; + surfaceWidth = texture.getWidth(0); + surfaceHeight = texture.getHeight(0); + + // 创建 Skia 上下文(如果需要) + if (directContext == null) { + directContext = DirectContext.makeGL(); + } + + try { + // 方法1:尝试创建 GPU 表面 + createGpuSurface(surfaceWidth, surfaceHeight); + } catch (Exception e) { + System.err.println("Failed to create GPU surface, falling back to CPU surface: " + e.getMessage()); + // 方法2:回退到 CPU 表面 + createCpuSurface(surfaceWidth, surfaceHeight); + } + + // 从 GpuTexture 加载数据到表面 + loadTextureToSurface(); + } + + /** + * 创建 GPU 表面(使用 OpenGL 纹理) + */ + private static void createGpuSurface(int width, int height) { + // 生成一个临时的 OpenGL 纹理供 Skia 使用 + int glTextureId = generateGlTexture(width, height); + + // 创建 Skia 渲染目标 + renderTarget = BackendRenderTarget.makeGL( + width, + height, + 0, // 采样数 + 8, // 模板位数 + glTextureId, + org.lwjgl.opengl.GL11.GL_RGBA8 + ); + + // 创建 Skia 表面 + skiaSurface = Surface.wrapBackendRenderTarget( + directContext, + renderTarget, + SurfaceOrigin.BOTTOM_LEFT, + SurfaceColorFormat.RGBA_8888, + ColorSpace.getSRGB() + ); + + } + + /** + * 创建 CPU 表面(纯软件渲染) + */ + private static void createCpuSurface(int width, int height) { + ImageInfo imageInfo = ImageInfo.makeS32(width, height, ColorAlphaType.PREMUL); + skiaSurface = Surface.makeRaster(imageInfo); + + } + + /** + * 生成临时的 OpenGL 纹理 + */ + private static int generateGlTexture(int width, int height) { + // 使用 LWJGL 直接创建 OpenGL 纹理 + int[] textures = new int[1]; + org.lwjgl.opengl.GL11.glGenTextures(textures); + int textureId = textures[0]; + + org.lwjgl.opengl.GL11.glBindTexture(org.lwjgl.opengl.GL11.GL_TEXTURE_2D, textureId); + + // 设置纹理参数 + org.lwjgl.opengl.GL11.glTexParameteri( + org.lwjgl.opengl.GL11.GL_TEXTURE_2D, + org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER, + org.lwjgl.opengl.GL11.GL_LINEAR + ); + org.lwjgl.opengl.GL11.glTexParameteri( + org.lwjgl.opengl.GL11.GL_TEXTURE_2D, + org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER, + org.lwjgl.opengl.GL11.GL_LINEAR + ); + + // 分配纹理存储 + org.lwjgl.opengl.GL11.glTexImage2D( + org.lwjgl.opengl.GL11.GL_TEXTURE_2D, + 0, + org.lwjgl.opengl.GL11.GL_RGBA8, + width, + height, + 0, + org.lwjgl.opengl.GL11.GL_RGBA, + org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE, + (ByteBuffer) null + ); + + org.lwjgl.opengl.GL11.glBindTexture(org.lwjgl.opengl.GL11.GL_TEXTURE_2D, 0); + + return textureId; + } + + /** + * 从 GpuTexture 加载数据到 Skia 表面 + */ + private static void loadTextureToSurface() { + if (currentTexture == null || skiaSurface == null) { + return; + } + + try { + // 读取 GpuTexture 数据到 ByteBuffer + ByteBuffer pixelData = ImageHelper.readGpuTextureDataCorrect(currentTexture); + if (pixelData == null) { + return; + } + + // 创建 Skia Image + ImageInfo imageInfo = ImageInfo.makeS32(surfaceWidth, surfaceHeight, ColorAlphaType.UNPREMUL); + Data data = Data.makeFromBytes(pixelData.array()); + + try (Image sourceImage = Image.makeRasterFromData(imageInfo, data, imageInfo.getMinRowBytes())) { + // 清除表面 + skiaSurface.getCanvas().clear(Color.makeARGB(0, 255, 255, 255)); //TRANSPARENT + + // 绘制到表面 + skiaSurface.getCanvas().drawImage(sourceImage, 0, 0); + + // 刷新 + if (directContext != null) { + directContext.flush(); + } + } + + } catch (Exception e) { + PupperLogger.error("SkiaContext", "image error:" + e); + } + } + + /** + * 执行绘制逻辑 + */ + public static void draw(Consumer drawingLogic) { + if (skiaSurface == null) { + throw new IllegalStateException("Skia surface not initialized. Call createSurfaceFromGpuTexture() first."); + } + + // 保存当前 OpenGL 状态 + saveGlState(); + + try { + // 获取画布 + Canvas canvas = getCanvas(); + if (canvas == null) { + return; + } + + // 执行用户绘制逻辑 + drawingLogic.accept(canvas); + + // 刷新上下文 + if (directContext != null) { + directContext.flush(); + } + + // 将结果保存回 GpuTexture + saveSurfaceToTexture(); + + } finally { + // 恢复 OpenGL 状态 + restoreGlState(); + } + } + + /** + * 将 Skia 表面内容保存回 GpuTexture + */ + private static void saveSurfaceToTexture() { + if (skiaSurface == null || currentTexture == null) { + return; + } + + try { + // 获取表面快照 + + try (Image snapshot = skiaSurface.makeImageSnapshot()) { + // 将 Skia Image 转换为 NativeImage + + try (NativeImage nativeImage = convertSkiaImageToNativeImage(snapshot)) { + // 使用 CommandEncoder 写入 GpuTexture + var device = RenderSystem.getDevice(); + var encoder = device.createCommandEncoder(); + + encoder.writeToTexture(currentTexture, nativeImage); + + } + } + + } catch (Exception e) { + PupperLogger.error("SkiaContext", "image error:" + e); + } + } + + /** + * 将 Skia Image 转换为 NativeImage + */ + @Unstable + private static NativeImage convertSkiaImageToNativeImage(Image skiaImage) { + return convertToNativeImageOptimized(skiaImage); + } + + /** + * 获取画布 + */ + @Nullable + public static Canvas getCanvas() { + return skiaSurface != null ? skiaSurface.getCanvas() : null; + } + + /** + * 获取 DirectContext + */ + public static DirectContext getContext() { + if (directContext == null) { + directContext = DirectContext.makeGL(); + } + return directContext; + } + + /** + * 清理资源 + */ + public static void cleanup() { + if (skiaSurface != null) { + skiaSurface.close(); + skiaSurface = null; + } + + if (renderTarget != null) { + renderTarget.close(); + renderTarget = null; + } + + if (currentNativeImage != null) { + currentNativeImage.close(); + currentNativeImage = null; + } + + currentTexture = null; + surfaceWidth = 0; + surfaceHeight = 0; + } + + /** + * 完全关闭(释放所有资源) + */ + public static void close() { + cleanup(); + + if (directContext != null) { + directContext.close(); + directContext = null; + } + } + + /** + * 保存当前 OpenGL 状态 + */ + private static void saveGlState() { + // 使用直接 OpenGL 调用保存状态 + // 注意:1.21.5 移除了很多 RenderSystem 方法 + int[] prevTexture = new int[1]; + org.lwjgl.opengl.GL11.glGetIntegerv(org.lwjgl.opengl.GL11.GL_TEXTURE_BINDING_2D, prevTexture); + + // 保存混合状态 + int[] blendEnabled = new int[1]; + org.lwjgl.opengl.GL11.glGetIntegerv(org.lwjgl.opengl.GL11.GL_BLEND, blendEnabled); + + // 保存视口 + int[] viewport = new int[4]; + org.lwjgl.opengl.GL11.glGetIntegerv(org.lwjgl.opengl.GL11.GL_VIEWPORT, viewport); + + // 存储到线程局部变量 + GlStateSavedState savedState = new GlStateSavedState(); + savedState.textureBinding = prevTexture[0]; + savedState.blendEnabled = blendEnabled[0] == org.lwjgl.opengl.GL11.GL_TRUE; + savedState.viewportX = viewport[0]; + savedState.viewportY = viewport[1]; + savedState.viewportWidth = viewport[2]; + savedState.viewportHeight = viewport[3]; + + ThreadLocalState.setState(savedState); + } + + /** + * 恢复 OpenGL 状态 + */ + private static void restoreGlState() { + GlStateSavedState savedState = ThreadLocalState.getState(); + if (savedState == null) { + return; + } + + // 恢复纹理绑定 + org.lwjgl.opengl.GL11.glBindTexture(org.lwjgl.opengl.GL11.GL_TEXTURE_2D, savedState.textureBinding); + + // 恢复混合状态 + if (savedState.blendEnabled) { + org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL11.GL_BLEND); + } else { + org.lwjgl.opengl.GL11.glDisable(org.lwjgl.opengl.GL11.GL_BLEND); + } + + // 恢复视口 + org.lwjgl.opengl.GL11.glViewport( + savedState.viewportX, + savedState.viewportY, + savedState.viewportWidth, + savedState.viewportHeight + ); + } + + /** + * 创建一个简单的表面(用于独立的 Skia 绘制) + */ + public static void createSimpleSurface(int width, int height) { + cleanup(); + + surfaceWidth = width; + surfaceHeight = height; + + // 创建 CPU 表面 + createCpuSurface(width, height); + } + + /** + * 获取表面尺寸 + */ + public static int[] getSurfaceSize() { + return new int[]{surfaceWidth, surfaceHeight}; + } + + /** + * 检查表面是否已创建 + */ + public static boolean isSurfaceCreated() { + return skiaSurface != null; + } + + /** + * 获取当前使用的纹理 + */ + @Nullable + public static GpuTexture getCurrentTexture() { + return currentTexture; + } + + /** + * 从 Minecraft 的帧缓冲创建表面 + */ + public static void createSurfaceFromFramebuffer() { + var framebuffer = MinecraftClient.getInstance().getFramebuffer(); + GpuTexture colorTexture = framebuffer.getColorAttachment(); + + if (colorTexture == null) { + throw new IllegalStateException("Framebuffer has no color attachment"); + } + + createSurfaceFromGpuTexture(colorTexture); + } + + // 线程局部状态保存 + private static class ThreadLocalState { + private static final ThreadLocal STATE = new ThreadLocal<>(); + + public static void setState(GlStateSavedState state) { + STATE.set(state); + } + + public static GlStateSavedState getState() { + return STATE.get(); + } + + public static void clear() { + STATE.remove(); + } + } + + // OpenGL 状态保存结构 + private static class GlStateSavedState { + int textureBinding = 0; + boolean blendEnabled = false; + int viewportX = 0; + int viewportY = 0; + int viewportWidth = 0; + int viewportHeight = 0; + } + + private static NativeImage convertToNativeImageOptimized(Image skiaImage) { + int width = skiaImage.getWidth(); + int height = skiaImage.getHeight(); + + NativeImage nativeImage = new NativeImage( + net.minecraft.client.texture.NativeImage.Format.RGBA, + width, + height, + false + ); + + try { + // 创建临时 Bitmap + Bitmap bitmap = new Bitmap(); + ImageInfo bitmapInfo = ImageInfo.makeS32(width, height, ColorAlphaType.UNPREMUL); + + if (!bitmap.allocPixels(bitmapInfo)) { + System.err.println("无法分配 Bitmap 内存"); + bitmap.close(); + return nativeImage; + } + + // 读取像素 + if (!skiaImage.readPixels(bitmap, 0, 0)) { + System.err.println("无法读取像素"); + bitmap.close(); + return nativeImage; + } + + // 获取像素缓冲区 + ByteBuffer pixelBuffer = bitmap.peekPixels(); + if (pixelBuffer == null) { + System.err.println("无法获取像素缓冲区"); + bitmap.close(); + return nativeImage; + } + + // 使用更高效的转换方法 + if (pixelBuffer.hasArray()) { + // 使用数组访问(最快) + byte[] pixelArray = pixelBuffer.array(); + int offset = pixelBuffer.arrayOffset(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int idx = offset + (y * width + x) * 4; + + int r = pixelArray[idx] & 0xFF; + int g = pixelArray[idx + 1] & 0xFF; + int b = pixelArray[idx + 2] & 0xFF; + int a = pixelArray[idx + 3] & 0xFF; + + int abgrColor = (a << 24) | (b << 16) | (g << 8) | r; + nativeImage.setColor(x, y, abgrColor); + } + } + } else { + // 使用 ByteBuffer 访问 + pixelBuffer.rewind(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int r = pixelBuffer.get() & 0xFF; + int g = pixelBuffer.get() & 0xFF; + int b = pixelBuffer.get() & 0xFF; + int a = pixelBuffer.get() & 0xFF; + + int abgrColor = (a << 24) | (b << 16) | (g << 8) | r; + nativeImage.setColor(x, y, abgrColor); + } + } + } + + bitmap.close(); - private static DirectContext context = null; - private static Surface surface; - private static BackendRenderTarget renderTarget; - - public static Canvas getCanvas() { - return surface.getCanvas(); - } - - public static void createSurface(int width, int height) { - - if (context == null) { - context = DirectContext.makeGL(); - } - - if (surface != null) { - surface.close(); - surface = null; - } - - if (renderTarget != null) { - renderTarget.close(); - renderTarget = null; - } - - renderTarget = BackendRenderTarget.makeGL(width, height, 0, 8, - MinecraftClient.getInstance().getFramebuffer().fbo, GL11.GL_RGBA8); - surface = Surface.wrapBackendRenderTarget(context, renderTarget, SurfaceOrigin.BOTTOM_LEFT, - SurfaceColorFormat.RGBA_8888, ColorSpace.getSRGB()); - } - - public static void draw(Consumer drawingLogic) { - - RenderSystem.pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); - RenderSystem.pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); - RenderSystem.pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); - RenderSystem.pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); - RenderSystem.clearColor(0f, 0f, 0f, 0f); - context.resetGLAll(); - - Canvas canvas = getCanvas(); - drawingLogic.accept(canvas); - - context.flush(); - - // BufferRenderer.reset(); - GL33.glBindSampler(0, 0); - // RenderSystem.disableBlend(); - GL11.glDisable(GL11.GL_BLEND); - // RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); - // RenderSystem.blendEquation(GL33.GL_FUNC_ADD); - GL33.glBlendEquation(GL33.GL_FUNC_ADD); - // RenderSystem.colorMask(true, true, true, true); - GL11.glColorMask(true, true, true, true); - // RenderSystem.depthMask(true); - GL11.glDepthMask(true); - //RenderSystem.disableScissor(); - GL11.glDisable(GL11.GL_SCISSOR_TEST); - GL11.glDisable(GL11.GL_STENCIL_TEST); - // RenderSystem.disableDepthTest(); - GL11.glDisable(GL11.GL_DEPTH_TEST); - GL13.glActiveTexture(GL13.GL_TEXTURE0); - //RenderSystem.activeTexture(GL13.GL_TEXTURE0); - //RenderSystem.disableCull(); - } - - public static DirectContext getContext() { - return context; - } + } catch (Exception e) { + e.printStackTrace(); + nativeImage.close(); + } + return nativeImage; + } } diff --git a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java index 1802296..e1a664f 100644 --- a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java +++ b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java @@ -531,6 +531,78 @@ public void preloadCommonResources() { } + @Unstable + public static ByteBuffer readGpuTextureDataCorrect(GpuTexture texture) { + if (texture == null) { + return null; + } + + RenderSystem.assertOnRenderThread(); + + try { + int width = texture.getWidth(0); + int height = texture.getHeight(0); + int pixelSize = texture.getFormat().pixelSize(); // 每个像素的字节数 + int bufferSize = width * height * pixelSize; + + // 创建缓冲区 + + // 使用 CountDownLatch 等待异步操作完成 + + try (GpuBuffer readBuffer = RenderSystem.getDevice().createBuffer( + () -> "GpuTextureReadBuffer", + BufferType.PIXEL_PACK, + BufferUsage.STREAM_READ, + bufferSize + )) { + CountDownLatch latch = new CountDownLatch(1); + ByteBuffer[] resultHolder = new ByteBuffer[1]; + // 创建命令编码器 + var commandEncoder = RenderSystem.getDevice().createCommandEncoder(); + + commandEncoder.copyTextureToBuffer( + texture, + readBuffer, + 0, + () -> { + try { + try (GpuBuffer.ReadView readView = commandEncoder.readBuffer(readBuffer)) { + + ByteBuffer pixelData = BufferUtils.createByteBuffer(bufferSize); + ByteBuffer sourceData = readView.data(); + if (sourceData != null) { + sourceData.rewind(); + pixelData.put(sourceData); + pixelData.flip(); + resultHolder[0] = pixelData; + } + } + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + } finally { + latch.countDown(); + } + }, + 0 // mipLevel + ); + + // 等待操作完成(最大等待5秒) + boolean success = latch.await(5, TimeUnit.SECONDS); + if (!success) { + System.err.println("读取纹理数据超时"); + return null; + } + + return resultHolder[0]; + + } + + } catch (Exception e) { + PupperLogger.error("ImageHelper", "image error:" + e); + return null; + } + } + public void close() { clearCache(); LOADER_EXECUTOR.shutdown(); @@ -545,7 +617,7 @@ public String toString() { fileImages, resourceImages, gpuTextureImages, loadingTasks ); } - } + } private static final ImageHelper INSTANCE = new ImageHelper(); From eeb59f7c863f3a0ffe7f1b7a907aebb114f09fd5 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 17:19:27 +0800 Subject: [PATCH 13/21] fix: set browserSettings null --- .../java/cn/pupperclient/libraries/browser/JCefBrowser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/cn/pupperclient/libraries/browser/JCefBrowser.java b/src/main/java/cn/pupperclient/libraries/browser/JCefBrowser.java index 4756451..7cfe24d 100644 --- a/src/main/java/cn/pupperclient/libraries/browser/JCefBrowser.java +++ b/src/main/java/cn/pupperclient/libraries/browser/JCefBrowser.java @@ -11,6 +11,7 @@ import net.ccbluex.liquidbounce.mcef.MCEFDownloadManager; import net.ccbluex.liquidbounce.mcef.MCEFPlatform; import net.ccbluex.liquidbounce.mcef.cef.MCEFBrowser; +import net.ccbluex.liquidbounce.mcef.cef.MCEFBrowserSettings; public class JCefBrowser { @@ -24,7 +25,7 @@ public static void init() { if(browser == null) { String url = "https://cn.bing.com"; boolean transparent = true; - browser = MCEF.INSTANCE.createBrowser(url, transparent, 60); + browser = MCEF.INSTANCE.createBrowser(url, transparent, null); browser.resize(1280, 720); } } From 3d39e4dec2357962dad23e6eb6862e15237d9e5c Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 17:21:29 +0800 Subject: [PATCH 14/21] fix(CapeRenderer): aljsdflhdsifh --- .../cn/pupperclient/management/cape/CapeRenderer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java b/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java index 2a9dd98..6454108 100644 --- a/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java +++ b/src/main/java/cn/pupperclient/management/cape/CapeRenderer.java @@ -20,8 +20,8 @@ public static void renderCapePreview(Identifier capeTexture, float x, float y, f var textureId = MinecraftClient.getInstance().getTextureManager().getTexture(capeTexture).getGlTexture(); // 尝试不同的纹理尺寸 - boolean loaded = Skia.getImageHelper().load(textureId, 64, 32, SurfaceOrigin.TOP_LEFT) || - Skia.getImageHelper().load(textureId, 128, 64, SurfaceOrigin.TOP_LEFT); + boolean loaded = Skia.getImageHelper().load(textureId, 64, 32) != null || + Skia.getImageHelper().load(textureId, 128, 64) != null; if (loaded) { Skia.save(); @@ -57,8 +57,8 @@ public static void renderRoundedCapePreview(Identifier capeTexture, float x, flo var textureId = MinecraftClient.getInstance().getTextureManager().getTexture(capeTexture).getGlTexture(); // 尝试不同的纹理尺寸 - boolean loaded = Skia.getImageHelper().load(textureId, 64, 32, SurfaceOrigin.TOP_LEFT) || - Skia.getImageHelper().load(textureId, 128, 64, SurfaceOrigin.TOP_LEFT); + boolean loaded = Skia.getImageHelper().load(textureId, 64, 32) != null || + Skia.getImageHelper().load(textureId, 128, 64) != null; if (loaded) { Path path = Path.makeRRect(RRect.makeXYWH(x, y, width, height, radius)); From 85ce7c89ed5f65d45b415b6f4d00d7a99d7d9470 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 17:32:45 +0800 Subject: [PATCH 15/21] fix(Command): aadadadadad --- .../pupperclient/gui/modmenu/pages/MusicPage.java | 14 ++++++-------- .../management/command/SoarCommand.java | 7 ++++--- .../management/command/impl/BindCommand.java | 4 ++-- .../management/command/impl/LoginCommand.java | 4 ++-- .../management/command/impl/MusicCommand.java | 14 +++++++------- .../management/mod/impl/hud/MusicInfoMod.java | 4 ++-- .../management/mod/impl/hud/WebBrowserMod.java | 2 +- .../mod/impl/render/ProjectileTrailMod.java | 2 +- .../minecraft/client/MixinMinecraftClient.java | 2 +- .../minecraft/client/gui/MixinSplashScreen.java | 2 +- .../mixins/minecraft/client/util/MixinWindow.java | 2 +- 11 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java b/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java index 91445bd..d4fadd1 100644 --- a/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java +++ b/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java @@ -264,12 +264,10 @@ private void refreshMusicList() { isRefreshing = true; - // 在后台线程中刷新音乐列表 new Thread(() -> { try { PupperClient.getInstance().getMusicManager().load(); - // 在主线程中更新UI cn.pupperclient.utils.Multithreading.runMainThread(() -> { this.init(); isRefreshing = false; @@ -313,7 +311,7 @@ private void drawRoundedImage(File file, float x, float y, float width, float he Skia.drawImage(file, x, y, width, height); - if (Skia.getImageHelper().load(file)) { + if (Skia.getImageHelper().load(file) != null) { Image image = Skia.getImageHelper().get(file.getName()); if (image != null) { Skia.getCanvas().drawImageRect(image, Rect.makeWH(image.getWidth(), image.getHeight()), @@ -324,12 +322,12 @@ private void drawRoundedImage(File file, float x, float y, float width, float he Skia.restore(); } - private class Item { + private static class Item { - private Music music; - private SimpleAnimation xAnimation = new SimpleAnimation(); - private SimpleAnimation yAnimation = new SimpleAnimation(); - private SimpleAnimation focusAnimation = new SimpleAnimation(); + private final Music music; + private final SimpleAnimation xAnimation = new SimpleAnimation(); + private final SimpleAnimation yAnimation = new SimpleAnimation(); + private final SimpleAnimation focusAnimation = new SimpleAnimation(); private Item(Music music) { this.music = music; diff --git a/src/main/java/cn/pupperclient/management/command/SoarCommand.java b/src/main/java/cn/pupperclient/management/command/SoarCommand.java index 9b8d221..f5329e5 100644 --- a/src/main/java/cn/pupperclient/management/command/SoarCommand.java +++ b/src/main/java/cn/pupperclient/management/command/SoarCommand.java @@ -185,9 +185,10 @@ private static MutableText createClickableText(String displayText, String comman return Text.literal(displayText) .formatted(color) .styled(style -> style - .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)) - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, - Text.literal(hoverText).formatted(Formatting.GRAY)))); + .withClickEvent(new ClickEvent.SuggestCommand(command)) + .withHoverEvent(new HoverEvent.ShowText( + Text.literal(hoverText).formatted(Formatting.GRAY) + ))); } private static String getShortModName(String fullName) { diff --git a/src/main/java/cn/pupperclient/management/command/impl/BindCommand.java b/src/main/java/cn/pupperclient/management/command/impl/BindCommand.java index 9345a74..70dec2c 100644 --- a/src/main/java/cn/pupperclient/management/command/impl/BindCommand.java +++ b/src/main/java/cn/pupperclient/management/command/impl/BindCommand.java @@ -140,8 +140,8 @@ private static void listKeybinds() { for (Mod mod : mods) { MutableText message = Text.literal("§b• " + mod.getName() + " §7→ §a" + keyName + " §7(keycode: " + keyCode + ")") .styled(style -> style - .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ".bind " + mod.getName() + " none")) - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + .withClickEvent(new ClickEvent.SuggestCommand( ".bind " + mod.getName() + " none")) + .withHoverEvent(new HoverEvent.ShowText( Text.literal("Click to clear this keybind").formatted(Formatting.GRAY)))); ChatUtils.addChatMessage(message); diff --git a/src/main/java/cn/pupperclient/management/command/impl/LoginCommand.java b/src/main/java/cn/pupperclient/management/command/impl/LoginCommand.java index 79cf759..df86e28 100644 --- a/src/main/java/cn/pupperclient/management/command/impl/LoginCommand.java +++ b/src/main/java/cn/pupperclient/management/command/impl/LoginCommand.java @@ -141,8 +141,8 @@ private static void phoneLoginWithCaptcha(String phone, String captcha) { // 提供重新发送验证码的快捷方式 MutableText retryText = Text.literal("§7[重新发送验证码]") .styled(style -> style - .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ".login send " + phone)) - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + .withClickEvent(new ClickEvent.SuggestCommand( ".login send " + phone)) + .withHoverEvent(new HoverEvent.ShowText( Text.literal("点击重新发送验证码")))); ChatUtils.addChatMessage(retryText); }); diff --git a/src/main/java/cn/pupperclient/management/command/impl/MusicCommand.java b/src/main/java/cn/pupperclient/management/command/impl/MusicCommand.java index 38301c8..b053a8c 100644 --- a/src/main/java/cn/pupperclient/management/command/impl/MusicCommand.java +++ b/src/main/java/cn/pupperclient/management/command/impl/MusicCommand.java @@ -159,8 +159,8 @@ private static void searchMusic(String keyword, int limit) { // 格式化显示 MutableText songText = Text.literal("§b" + (i + 1) + ". §f" + songName) .styled(style -> style - .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ".music download " + songId)) - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + .withClickEvent(new ClickEvent.SuggestCommand( ".music download " + songId)) + .withHoverEvent(new HoverEvent.ShowText( Text.literal("§6点击快速下载\n§7歌手: " + artistsStr + "\n§7专辑: " + albumName)))); MutableText artistText = Text.literal(" §7- " + artistsStr); @@ -350,8 +350,7 @@ private static void listDownloadedMusic() { MutableText fileText = Text.literal("§b" + (i + 1) + ". §f" + displayName) .styled(style -> style - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, - Text.literal("§6文件: " + fileName + "\n§7大小: " + formatFileSize(musicFile.length()))))); + .withHoverEvent(new HoverEvent.ShowText(Text.literal("§6文件: " + fileName + "\n§7大小: " + formatFileSize(musicFile.length()))))); ChatUtils.addChatMessage(fileText); } @@ -485,8 +484,9 @@ private static MutableText createClickableText(String command, String hoverText) return Text.literal(" [下载]") .formatted(Formatting.GREEN) .styled(style -> style - .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)) - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, - Text.literal(hoverText).formatted(Formatting.GRAY)))); + .withClickEvent(new ClickEvent.SuggestCommand(command)) + .withHoverEvent(new HoverEvent.ShowText( + Text.literal(hoverText).formatted(Formatting.GRAY) + ))); } } diff --git a/src/main/java/cn/pupperclient/management/mod/impl/hud/MusicInfoMod.java b/src/main/java/cn/pupperclient/management/mod/impl/hud/MusicInfoMod.java index f47556f..4a848ce 100644 --- a/src/main/java/cn/pupperclient/management/mod/impl/hud/MusicInfoMod.java +++ b/src/main/java/cn/pupperclient/management/mod/impl/hud/MusicInfoMod.java @@ -318,7 +318,7 @@ private void drawInfo(float width, float height) { String albumPath = m.getAlbum().getAbsolutePath(); if (!albumPath.equals(currentAlbumPath)) { currentAlbumPath = albumPath; - if (Skia.getImageHelper().load(m.getAlbum())) { + if (Skia.getImageHelper().load(m.getAlbum()) != null) { Image image = Skia.getImageHelper().get(m.getAlbum().getName()); if (image != null) { albumBitmap = new Bitmap(); @@ -464,7 +464,7 @@ private static float getDynamicPulseMagnitude(float[] spectrum) { private void drawBlurredImage(File file, float x, float y, float width, float height) { Paint blurPaint = new Paint(); blurPaint.setImageFilter(ImageFilter.makeBlur(DEFAULT_BLUR_RADIUS, DEFAULT_BLUR_RADIUS, FilterTileMode.REPEAT)); - if (Skia.getImageHelper().load(file)) { + if (Skia.getImageHelper().load(file) != null) { Image image = Skia.getImageHelper().get(file.getName()); if (image != null) { Skia.getCanvas().drawImageRect(image, Rect.makeWH(image.getWidth(), image.getHeight()), diff --git a/src/main/java/cn/pupperclient/management/mod/impl/hud/WebBrowserMod.java b/src/main/java/cn/pupperclient/management/mod/impl/hud/WebBrowserMod.java index c30cf3e..e13097d 100644 --- a/src/main/java/cn/pupperclient/management/mod/impl/hud/WebBrowserMod.java +++ b/src/main/java/cn/pupperclient/management/mod/impl/hud/WebBrowserMod.java @@ -34,7 +34,7 @@ public WebBrowserMod() { if (MCEF.INSTANCE.isInitialized() && JCefBrowser.getBrowser() != null) { MCEF.INSTANCE.getApp().getHandle().N_DoMessageLoopWork(); this.drawBlurBackground(getX(), getY(), currentWidth, currentHeight); - Skia.drawRoundedImage(JCefBrowser.getBrowser().getRenderer().getTextureID(), getX(), getY(), currentWidth, currentHeight, + Skia.drawRoundedImage(JCefBrowser.getBrowser().getRenderer().getTexture(), getX(), getY(), currentWidth, currentHeight, getRadius(), opacitySetting.getValue()); } diff --git a/src/main/java/cn/pupperclient/management/mod/impl/render/ProjectileTrailMod.java b/src/main/java/cn/pupperclient/management/mod/impl/render/ProjectileTrailMod.java index 670bd13..3b1cdf6 100644 --- a/src/main/java/cn/pupperclient/management/mod/impl/render/ProjectileTrailMod.java +++ b/src/main/java/cn/pupperclient/management/mod/impl/render/ProjectileTrailMod.java @@ -47,7 +47,7 @@ public ProjectileTrailMod() { ParticleType type = getCurrentType(); if (type != null && type instanceof ParticleEffect) { - mc.world.addParticle((ParticleEffect) type, projectile.getX(), projectile.getY(), + mc.world.addParticleClient((ParticleEffect) type, projectile.getX(), projectile.getY(), projectile.getZ(), 0.0, 0.0, 0.0); } } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java index b888cce..a8854be 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java @@ -133,7 +133,7 @@ public void updateWindowTitle() { @Inject(method = "", at = @At("TAIL")) public void init(CallbackInfo ci) throws IOException { - SkiaContext.createSurface(window.getWidth(), window.getHeight()); + SkiaContext.createSimpleSurface(window.getWidth(), window.getHeight()); PupperClient.getInstance().start(); } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java index c03af05..7e40d86 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/gui/MixinSplashScreen.java @@ -74,7 +74,7 @@ private void pupper_takeOverAndRender(DrawContext context, int mouseX, int mouse // Recreate surface if window size changed if (lastWindowWidth != width || lastWindowHeight != height) { - SkiaContext.createSurface(width, height); + SkiaContext.createSimpleSurface(width, height); lastWindowWidth = width; lastWindowHeight = height; } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java index c5637d3..2313db2 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java @@ -24,7 +24,7 @@ public class MixinWindow { @Inject(method = "onFramebufferSizeChanged", at = @At("RETURN")) private void onFramebufferSizeChanged(long window, int width, int height, CallbackInfo ci) { - SkiaContext.createSurface(width, height); + SkiaContext.createSimpleSurface(width, height); } @Inject(method = "", at = @At("RETURN")) From b3fb124ff245cae16d385165c7fc48ba490148a5 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 18:33:54 +0800 Subject: [PATCH 16/21] fix(CapeManager): Use MinecraftClient.getInstance().execute() --- .../management/cape/CapeManager.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/cn/pupperclient/management/cape/CapeManager.java b/src/main/java/cn/pupperclient/management/cape/CapeManager.java index 17b6c9b..12baafd 100644 --- a/src/main/java/cn/pupperclient/management/cape/CapeManager.java +++ b/src/main/java/cn/pupperclient/management/cape/CapeManager.java @@ -52,15 +52,20 @@ public void loadCape(String id, byte[] textureData) { if (id == null || textureData == null) return; executorService.submit(() -> { - RenderSystem.recordRenderCall(() -> { - NativeImageBackedTexture nativeImage = createNativeTexture(textureData); - if (nativeImage != null) { + // 在后台线程加载纹理数据 + NativeImageBackedTexture nativeImage = createNativeTexture(textureData); + if (nativeImage != null) { + // 获取Minecraft客户端实例 + MinecraftClient client = MinecraftClient.getInstance(); + + // 将纹理注册提交到渲染线程 + client.execute(() -> { Identifier identifier = Identifier.of("pupper", namespace + "/" + id); - MinecraftClient.getInstance().getTextureManager().registerTexture(identifier, nativeImage); + client.getTextureManager().registerTexture(identifier, nativeImage); loadedCapes.put(id, identifier); loadedCapeTextures.put(identifier, nativeImage); - } - }); + }); + } }); } @@ -92,7 +97,7 @@ public Set getLoadedCapeIds() { private static NativeImageBackedTexture createNativeTexture(byte[] bytes) { if (bytes == null) return null; try { - return new NativeImageBackedTexture(NativeImage.read(bytes)); + return new NativeImageBackedTexture(() -> "Native_Texture",NativeImage.read(bytes)); } catch (IOException e) { throw new RuntimeException(e); } From d873fef731a4f7f0c40c610bc12ce44cdc6bcd63 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 18:36:01 +0800 Subject: [PATCH 17/21] fix: Correct viafabricplus mod version for 1.21.5 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index bdd9fdc..65acebc 100644 --- a/build.gradle +++ b/build.gradle @@ -37,13 +37,13 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" - modImplementation "com.viaversion:viafabricplus-api:4.4.1" + modImplementation "com.viaversion:viafabricplus-api:4.1.5" modRuntimeOnly "maven.modrinth:sodium:mc1.21.5-0.6.13-fabric" modRuntimeOnly "maven.modrinth:iris:1.8.11+1.21.5-fabric" modRuntimeOnly "maven.modrinth:sodium-extra:mc1.21.5-0.6.3+fabric" modRuntimeOnly "maven.modrinth:lithium:mc1.21.5-0.16.3-fabric" modImplementation "maven.modrinth:in-game-account-switcher:Rqmwlwr6" - modImplementation "com.viaversion:viafabricplus:4.4.1" + modImplementation "com.viaversion:viafabricplus:4.1.5" modRuntimeOnly "maven.modrinth:immediatelyfast:1.9.7+1.21.5-fabric" modRuntimeOnly "maven.modrinth:entityculling:ldvBBWG2" modImplementation "maven.modrinth:modmenu:14.0.0" From c4523151b22138fc618d74b2c16f6dbdc0f3e2e2 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 18:36:59 +0800 Subject: [PATCH 18/21] fix: remove BufferRendererAccessor --- src/main/resources/pupper.mixins.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/pupper.mixins.json b/src/main/resources/pupper.mixins.json index 0b4d894..17dfd8a 100644 --- a/src/main/resources/pupper.mixins.json +++ b/src/main/resources/pupper.mixins.json @@ -15,7 +15,6 @@ "minecraft.client.gui.MixinSplashScreen", "minecraft.client.gui.MixinTitleScreen", "minecraft.client.option.MixinKeyBinding", - "minecraft.client.render.BufferRendererAccessor", "minecraft.client.render.MixinCamera", "minecraft.client.render.MixinEntityRenderer", "minecraft.client.render.MixinGameRenderer", From 88ca13f5a745c5b1ef06da5699138a7c50713413 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 19:37:55 +0800 Subject: [PATCH 19/21] fix: render asdasdasdsdfdpasdpfijdsiij --- build.gradle | 2 +- .../management/cape/CapeManager.java | 20 ++- .../client/MixinMinecraftClient.java | 11 +- .../minecraft/client/util/MixinWindow.java | 6 +- .../skia/context/SkiaContext.java | 152 +++++++++++------- .../pupperclient/skia/image/ImageHelper.java | 47 +++--- 6 files changed, 135 insertions(+), 103 deletions(-) diff --git a/build.gradle b/build.gradle index 65acebc..3d6815b 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ dependencies { modImplementation "maven.modrinth:in-game-account-switcher:Rqmwlwr6" modImplementation "com.viaversion:viafabricplus:4.1.5" modRuntimeOnly "maven.modrinth:immediatelyfast:1.9.7+1.21.5-fabric" - modRuntimeOnly "maven.modrinth:entityculling:ldvBBWG2" + // modRuntimeOnly "maven.modrinth:entityculling:ldvBBWG2" modImplementation "maven.modrinth:modmenu:14.0.0" jij 'io.github.smartboot.socket:aio-core:1.7.1' diff --git a/src/main/java/cn/pupperclient/management/cape/CapeManager.java b/src/main/java/cn/pupperclient/management/cape/CapeManager.java index 12baafd..8895f1b 100644 --- a/src/main/java/cn/pupperclient/management/cape/CapeManager.java +++ b/src/main/java/cn/pupperclient/management/cape/CapeManager.java @@ -1,5 +1,6 @@ package cn.pupperclient.management.cape; +import cn.pupperclient.utils.Multithreading; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.MinecraftClient; import net.minecraft.client.texture.NativeImage; @@ -52,19 +53,16 @@ public void loadCape(String id, byte[] textureData) { if (id == null || textureData == null) return; executorService.submit(() -> { - // 在后台线程加载纹理数据 NativeImageBackedTexture nativeImage = createNativeTexture(textureData); if (nativeImage != null) { - // 获取Minecraft客户端实例 - MinecraftClient client = MinecraftClient.getInstance(); - - // 将纹理注册提交到渲染线程 - client.execute(() -> { - Identifier identifier = Identifier.of("pupper", namespace + "/" + id); - client.getTextureManager().registerTexture(identifier, nativeImage); - loadedCapes.put(id, identifier); - loadedCapeTextures.put(identifier, nativeImage); - }); + Multithreading.runMainThread( + () -> { + Identifier identifier = Identifier.of("pupper", namespace + "/" + id); + MinecraftClient.getInstance().getTextureManager().registerTexture(identifier, nativeImage); + loadedCapes.put(id, identifier); + loadedCapeTextures.put(identifier, nativeImage); + } + ); } }); } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java index a8854be..595a7cf 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java @@ -5,6 +5,7 @@ import cn.pupperclient.event.client.WorldChangeEvent; import cn.pupperclient.shader.impl.Kawaseblur; +import cn.pupperclient.skia.Skia; import net.minecraft.client.gui.screen.Screen; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; @@ -133,8 +134,14 @@ public void updateWindowTitle() { @Inject(method = "", at = @At("TAIL")) public void init(CallbackInfo ci) throws IOException { - SkiaContext.createSimpleSurface(window.getWidth(), window.getHeight()); - PupperClient.getInstance().start(); + int width = window.getWidth(); + int height = window.getHeight(); + if (width > 0 && height > 0) { + SkiaContext.createSimpleSurface(width, height); + } else { + PupperClient.LOGGER.warn("Window size during init is invalid: {}x{}", width, height); + } + PupperClient.getInstance().start(); } @Inject(method = "stop", at = @At("HEAD")) diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java index 2313db2..47f219f 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java @@ -24,7 +24,11 @@ public class MixinWindow { @Inject(method = "onFramebufferSizeChanged", at = @At("RETURN")) private void onFramebufferSizeChanged(long window, int width, int height, CallbackInfo ci) { - SkiaContext.createSimpleSurface(width, height); + if (width > 0 && height > 0) { + SkiaContext.createSimpleSurface(width, height); + } else { + PupperClient.LOGGER.warn("Window size is invalid: {}x{}, skipping surface creation", width, height); + } } @Inject(method = "", at = @At("RETURN")) diff --git a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java index 269b4ff..076ddae 100644 --- a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java +++ b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java @@ -66,6 +66,11 @@ public static void createSurfaceFromGpuTexture(GpuTexture texture) { * 创建 GPU 表面(使用 OpenGL 纹理) */ private static void createGpuSurface(int width, int height) { + if (width <= 0 || height <= 0) { + PupperLogger.warn("SkiaContext", "Attempted to create GPU surface with invalid dimensions: " + width + "x" + height); + return; + } + // 生成一个临时的 OpenGL 纹理供 Skia 使用 int glTextureId = generateGlTexture(width, height); @@ -87,16 +92,23 @@ private static void createGpuSurface(int width, int height) { SurfaceColorFormat.RGBA_8888, ColorSpace.getSRGB() ); - } /** * 创建 CPU 表面(纯软件渲染) */ private static void createCpuSurface(int width, int height) { - ImageInfo imageInfo = ImageInfo.makeS32(width, height, ColorAlphaType.PREMUL); - skiaSurface = Surface.makeRaster(imageInfo); + if (width <= 0 || height <= 0) { + PupperLogger.warn("SkiaContext", "Attempted to create CPU surface with invalid dimensions: " + width + "x" + height); + return; + } + try { + ImageInfo imageInfo = ImageInfo.makeS32(width, height, ColorAlphaType.PREMUL); + skiaSurface = Surface.makeRaster(imageInfo); + } catch (Exception e) { + PupperLogger.error("SkiaContext", "Failed to create CPU surface: " + e); + } } /** @@ -173,7 +185,7 @@ private static void loadTextureToSurface() { } } catch (Exception e) { - PupperLogger.error("SkiaContext", "image error:" + e); + PupperLogger.error("SkiaContext", "loadTextureToSurface error:" + e); } } @@ -182,10 +194,10 @@ private static void loadTextureToSurface() { */ public static void draw(Consumer drawingLogic) { if (skiaSurface == null) { - throw new IllegalStateException("Skia surface not initialized. Call createSurfaceFromGpuTexture() first."); + PupperLogger.warn("SkiaContext", "Skia surface not initialized. Drawing skipped."); + return; } - // 保存当前 OpenGL 状态 saveGlState(); try { @@ -237,7 +249,7 @@ private static void saveSurfaceToTexture() { } } catch (Exception e) { - PupperLogger.error("SkiaContext", "image error:" + e); + PupperLogger.error("SkiaContext", "saveSurfaceToTexture error:" + e); } } @@ -246,7 +258,7 @@ private static void saveSurfaceToTexture() { */ @Unstable private static NativeImage convertSkiaImageToNativeImage(Image skiaImage) { - return convertToNativeImageOptimized(skiaImage); + return convertToNativeImage(skiaImage); } /** @@ -364,6 +376,12 @@ private static void restoreGlState() { * 创建一个简单的表面(用于独立的 Skia 绘制) */ public static void createSimpleSurface(int width, int height) { + if (width <= 0 || height <= 0) { + PupperLogger.warn("SkiaContext", "Attempted to create simple surface with invalid dimensions: " + width + "x" + height); + cleanup(); // 清理旧资源 + return; + } + cleanup(); surfaceWidth = width; @@ -436,85 +454,97 @@ private static class GlStateSavedState { int viewportHeight = 0; } - private static NativeImage convertToNativeImageOptimized(Image skiaImage) { + private static NativeImage convertToNativeImage(Image skiaImage) { int width = skiaImage.getWidth(); int height = skiaImage.getHeight(); - NativeImage nativeImage = new NativeImage( - net.minecraft.client.texture.NativeImage.Format.RGBA, - width, - height, - false - ); - try { - // 创建临时 Bitmap + // 创建 Bitmap Bitmap bitmap = new Bitmap(); ImageInfo bitmapInfo = ImageInfo.makeS32(width, height, ColorAlphaType.UNPREMUL); if (!bitmap.allocPixels(bitmapInfo)) { - System.err.println("无法分配 Bitmap 内存"); - bitmap.close(); - return nativeImage; + PupperLogger.error("convertToNativeImage","无法分配 Bitmap 像素"); + return null; } // 读取像素 if (!skiaImage.readPixels(bitmap, 0, 0)) { - System.err.println("无法读取像素"); + PupperLogger.error("convertToNativeImage","无法读取像素到 Bitmap"); bitmap.close(); - return nativeImage; + return null; } - // 获取像素缓冲区 + // 创建 NativeImage + NativeImage nativeImage = new NativeImage( + net.minecraft.client.texture.NativeImage.Format.RGBA, + width, + height, + false + ); + + // 获取 Bitmap 的像素数据 ByteBuffer pixelBuffer = bitmap.peekPixels(); if (pixelBuffer == null) { - System.err.println("无法获取像素缓冲区"); + PupperLogger.error("convertToNativeImage","无法获取 Bitmap 像素缓冲区"); bitmap.close(); - return nativeImage; + nativeImage.close(); + return null; } - // 使用更高效的转换方法 - if (pixelBuffer.hasArray()) { - // 使用数组访问(最快) - byte[] pixelArray = pixelBuffer.array(); - int offset = pixelBuffer.arrayOffset(); + // 批量转换像素(更高效) + convertPixelBufferBatch(pixelBuffer, nativeImage, width, height); + + bitmap.close(); + return nativeImage; + + } catch (Exception e) { + PupperLogger.error("convertToNativeImage","error: " + e); + return null; + } + } + + private static void convertPixelBufferBatch( + ByteBuffer pixelBuffer, + NativeImage nativeImage, + int width, + int height) { + + // 重置缓冲区位置 + pixelBuffer.rewind(); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int idx = offset + (y * width + x) * 4; + // 直接操作底层字节数组(如果可能) + if (pixelBuffer.hasArray()) { + byte[] pixelArray = pixelBuffer.array(); + int arrayOffset = pixelBuffer.arrayOffset(); - int r = pixelArray[idx] & 0xFF; - int g = pixelArray[idx + 1] & 0xFF; - int b = pixelArray[idx + 2] & 0xFF; - int a = pixelArray[idx + 3] & 0xFF; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int baseIndex = (y * width + x) * 4 + arrayOffset; - int abgrColor = (a << 24) | (b << 16) | (g << 8) | r; - nativeImage.setColor(x, y, abgrColor); - } + int r = pixelArray[baseIndex] & 0xFF; + int g = pixelArray[baseIndex + 1] & 0xFF; + int b = pixelArray[baseIndex + 2] & 0xFF; + int a = pixelArray[baseIndex + 3] & 0xFF; + + int nativeColor = (a << 24) | (b << 16) | (g << 8) | r; + nativeImage.setColor(x, y, nativeColor); } - } else { - // 使用 ByteBuffer 访问 - pixelBuffer.rewind(); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int r = pixelBuffer.get() & 0xFF; - int g = pixelBuffer.get() & 0xFF; - int b = pixelBuffer.get() & 0xFF; - int a = pixelBuffer.get() & 0xFF; - - int abgrColor = (a << 24) | (b << 16) | (g << 8) | r; - nativeImage.setColor(x, y, abgrColor); - } + } + } else { + // 如果没有数组,使用 ByteBuffer 操作 + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int r = pixelBuffer.get() & 0xFF; + int g = pixelBuffer.get() & 0xFF; + int b = pixelBuffer.get() & 0xFF; + int a = pixelBuffer.get() & 0xFF; + + int nativeColor = (a << 24) | (b << 16) | (g << 8) | r; + nativeImage.setColor(x, y, nativeColor); } } - - bitmap.close(); - - } catch (Exception e) { - e.printStackTrace(); - nativeImage.close(); } - return nativeImage; } + } diff --git a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java index e1a664f..6f6da12 100644 --- a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java +++ b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java @@ -13,7 +13,7 @@ import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.util.Identifier; -import org.jetbrains.annotations.Blocking; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.BufferUtils; @@ -78,7 +78,7 @@ public CompletableFuture loadAsync(GpuTexture texture) { return image; } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/loadAsync(GpuTexture)", "CompletableFuture error:" + e); return null; } }, LOADER_EXECUTOR); @@ -129,7 +129,7 @@ public Image load(GpuTexture texture, float width, float height) { return image; } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/load(GpuTexture)", " error:" + e); return null; } } @@ -164,7 +164,7 @@ public CompletableFuture loadAsync(String filePath) { return image; } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/loadAsync(filePath)", "loadAsync CompletableFuture error:" + e); return null; } }, LOADER_EXECUTOR); @@ -229,7 +229,7 @@ public CompletableFuture loadAsync(Identifier identifier) { } } catch (IOException e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/loadAsync(identifier)", "error:" + e); return null; } }, LOADER_EXECUTOR); @@ -257,7 +257,7 @@ public Image load(String filePath) { return image; } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/load(filePath)", "error:" + e); return null; } } @@ -280,7 +280,7 @@ public Image load(File file) { return image; } catch (IOException e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/load(file)", "error:" + e); return null; } } @@ -316,7 +316,7 @@ public Image load(Identifier identifier) { } } catch (IOException e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/load(identifier)", "error:" + e); return null; } } @@ -363,14 +363,13 @@ public static ByteBuffer readGpuTextureData(GpuTexture texture) { CountDownLatch latch = new CountDownLatch(1); // 创建 GPU 缓冲区用于读取 - GpuBuffer readBuffer = RenderSystem.getDevice().createBuffer( + + try (GpuBuffer readBuffer = RenderSystem.getDevice().createBuffer( () -> "TextureReadBuffer", BufferType.PIXEL_PACK, BufferUsage.STREAM_READ, bufferSize - ); - - try { + )) { // 创建命令编码器 var commandEncoder = RenderSystem.getDevice().createCommandEncoder(); @@ -393,9 +392,9 @@ public static ByteBuffer readGpuTextureData(GpuTexture texture) { result.set(pixelData); } } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/readGpuTextureData", "copyTextureToBuffer error:" + e); } finally { - latch.countDown(); // 通知主线程数据已读取完成 + latch.countDown(); } }, 0 // mipLevel @@ -404,18 +403,16 @@ public static ByteBuffer readGpuTextureData(GpuTexture texture) { // 等待数据读取完成 boolean success = latch.await(5, TimeUnit.SECONDS); if (!success) { - System.err.println("读取纹理数据超时"); + PupperLogger.error("ImageHelper/readGpuTextureData", "读取纹理数据超时"); return null; } return result.get(); - } finally { - readBuffer.close(); } } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/readGpuTextureData", "error:" + e); return null; } } @@ -452,7 +449,7 @@ public GpuTexture convertToGpuTexture(Image image) { return texture; } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/convertToGpuTexture", "error:" + e); return null; } } @@ -545,10 +542,6 @@ public static ByteBuffer readGpuTextureDataCorrect(GpuTexture texture) { int pixelSize = texture.getFormat().pixelSize(); // 每个像素的字节数 int bufferSize = width * height * pixelSize; - // 创建缓冲区 - - // 使用 CountDownLatch 等待异步操作完成 - try (GpuBuffer readBuffer = RenderSystem.getDevice().createBuffer( () -> "GpuTextureReadBuffer", BufferType.PIXEL_PACK, @@ -578,7 +571,7 @@ public static ByteBuffer readGpuTextureDataCorrect(GpuTexture texture) { } } } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/readGpuTextureDataCorrect", "copyTextureToBuffer error:" + e); } finally { latch.countDown(); } @@ -589,7 +582,7 @@ public static ByteBuffer readGpuTextureDataCorrect(GpuTexture texture) { // 等待操作完成(最大等待5秒) boolean success = latch.await(5, TimeUnit.SECONDS); if (!success) { - System.err.println("读取纹理数据超时"); + PupperLogger.error("ImageHelper/readGpuTextureDataCorrect", "读取纹理数据超时"); return null; } @@ -598,7 +591,7 @@ public static ByteBuffer readGpuTextureDataCorrect(GpuTexture texture) { } } catch (Exception e) { - PupperLogger.error("ImageHelper", "image error:" + e); + PupperLogger.error("ImageHelper/readGpuTextureDataCorrect", "error:" + e); return null; } } @@ -611,7 +604,7 @@ public void close() { public record CacheStats(int fileImages, int resourceImages, int gpuTextureImages, int loadingTasks) { @Override - public String toString() { + public @NotNull String toString() { return String.format( "CacheStats{文件图像=%d, 资源图像=%d, GPU纹理=%d, 加载任务=%d}", fileImages, resourceImages, gpuTextureImages, loadingTasks From aefeac79dce88554378144133f4554977f254b86 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sat, 3 Jan 2026 20:20:35 +0800 Subject: [PATCH 20/21] =?UTF-8?q?fix:=20shader=20no=20fix=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/pupperclient/PupperClient.java | 5 ++ .../cn/pupperclient/shader/MeshBuilder.java | 24 +++++++--- .../shader/PupperRenderPipelines.java | 47 +++++++------------ .../shader/VertexFormatElements.java | 28 +++++++++++ .../pupperclient/shader/impl/Kawaseblur.java | 30 +++++++----- .../resources/assets/pupper/shaders/blur.vert | 9 ++-- .../assets/pupper/shaders/passthrough.vert | 3 +- 7 files changed, 93 insertions(+), 53 deletions(-) create mode 100644 src/main/java/cn/pupperclient/shader/VertexFormatElements.java diff --git a/src/main/java/cn/pupperclient/PupperClient.java b/src/main/java/cn/pupperclient/PupperClient.java index c8183f9..7215137 100644 --- a/src/main/java/cn/pupperclient/PupperClient.java +++ b/src/main/java/cn/pupperclient/PupperClient.java @@ -26,6 +26,7 @@ import com.viaversion.viafabricplus.api.ViaFabricPlusBase; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.minecraft.util.Identifier; import org.apache.logging.log4j.Logger; import java.io.IOException; @@ -240,4 +241,8 @@ public enum MusicToolStatus { FAILED, DONE } + + public static Identifier identifier(String path) { + return Identifier.of("pupper", path); + } } diff --git a/src/main/java/cn/pupperclient/shader/MeshBuilder.java b/src/main/java/cn/pupperclient/shader/MeshBuilder.java index fd42c18..572aa31 100644 --- a/src/main/java/cn/pupperclient/shader/MeshBuilder.java +++ b/src/main/java/cn/pupperclient/shader/MeshBuilder.java @@ -14,11 +14,12 @@ public class MeshBuilder { private final VertexFormat format; private final VertexFormat.DrawMode drawMode; - private ByteBuffer vertices; - private long verticesPointerStart, verticesPointer; + private final ByteBuffer vertices; + private final long verticesPointerStart; + private long verticesPointer; - private ByteBuffer indices; - private long indicesPointer; + private final ByteBuffer indices; + private final long indicesPointer; private int vertexI, indicesCount; private boolean building; @@ -31,7 +32,7 @@ public MeshBuilder(VertexFormat format, VertexFormat.DrawMode drawMode) { vertices = BufferUtils.createByteBuffer(vertexSize * 256 * 4); verticesPointerStart = memAddress0(vertices); - indices = BufferUtils.createByteBuffer(6 * 512 * 4); // 假设四边形 + indices = BufferUtils.createByteBuffer(6 * 512 * 4); indicesPointer = memAddress0(indices); } @@ -44,10 +45,21 @@ public void begin() { building = true; } - public MeshBuilder vec2(float x, float y) { + // 添加位置 (x, y, z) + public MeshBuilder vec3(float x, float y, float z) { long p = verticesPointer; memPutFloat(p, x); memPutFloat(p + 4, y); + memPutFloat(p + 8, z); + verticesPointer += 12; + return this; + } + + // 添加纹理坐标 (u, v) + public MeshBuilder vec2(float u, float v) { + long p = verticesPointer; + memPutFloat(p, u); + memPutFloat(p + 4, v); verticesPointer += 8; return this; } diff --git a/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java b/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java index a92847a..a132c10 100644 --- a/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java +++ b/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java @@ -1,5 +1,6 @@ package cn.pupperclient.shader; +import cn.pupperclient.PupperClient; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.platform.DepthTestFunction; @@ -9,6 +10,7 @@ import com.mojang.blaze3d.vertex.VertexFormatElement; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.UniformType; +import net.minecraft.client.render.VertexFormats; import net.minecraft.resource.ResourceManager; import net.minecraft.resource.SynchronousResourceReloader; import net.minecraft.util.Identifier; @@ -22,20 +24,18 @@ public class PupperRenderPipelines { private static final List PIPELINES = new ArrayList<>(); - public static final VertexFormat POS2 = VertexFormat.builder() - .add("POS2", VertexFormatElement.POSITION) - .build(); - - public static final VertexFormat POS2_TEXTURE = VertexFormat.builder() - .add("POS2_TEXTURE", VertexFormatElement.POSITION) - .add("UV0", VertexFormatElement.UV0) - .build(); + // 着色器代码缓存 + private static final Identifier BLUR_VERT = PupperClient.identifier("shaders/blur.vert"); + private static final Identifier BLUR_DOWN_FRAG = PupperClient.identifier("shaders/blur_down.frag"); + private static final Identifier BLUR_UP_FRAG = PupperClient.identifier("shaders/blur_up.frag"); + private static final Identifier PASSTHROUGH_VERT = PupperClient.identifier("shaders/passthrough.vert"); + private static final Identifier PASSTHROUGH_FRAG = PupperClient.identifier("shaders/passthrough.frag"); // 着色器管线定义 public static final RenderPipeline BLUR_DOWN = register(new RenderPipeline.Builder() - .withVertexShader(read("blur.vert")) - .withFragmentShader(read("blur_down.frag")) - .withVertexFormat(POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withVertexShader(BLUR_VERT) + .withFragmentShader(BLUR_DOWN_FRAG) + .withVertexFormat(VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLES) .withSampler("uTexture") .withUniform("uHalfTexelSize", UniformType.VEC2) .withUniform("uOffset", UniformType.FLOAT) @@ -46,9 +46,9 @@ public class PupperRenderPipelines { .build()); public static final RenderPipeline BLUR_UP = register(new RenderPipeline.Builder() - .withVertexShader(read("blur.vert")) - .withFragmentShader(read("blur_up.frag")) - .withVertexFormat(POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withVertexShader(BLUR_VERT) + .withFragmentShader(BLUR_UP_FRAG) + .withVertexFormat(VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLES) .withSampler("uTexture") .withUniform("uHalfTexelSize", UniformType.VEC2) .withUniform("uOffset", UniformType.FLOAT) @@ -59,9 +59,9 @@ public class PupperRenderPipelines { .build()); public static final RenderPipeline PASSTHROUGH = register(new RenderPipeline.Builder() - .withVertexShader(read("passthrough.vert")) - .withFragmentShader(read("passthrough.frag")) - .withVertexFormat(POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withVertexShader(PASSTHROUGH_VERT) + .withFragmentShader(PASSTHROUGH_FRAG) + .withVertexFormat(VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLES) .withSampler("uTexture") .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) .withDepthWrite(false) @@ -74,7 +74,7 @@ private static RenderPipeline register(RenderPipeline pipeline) { return pipeline; } - public class Reloader implements SynchronousResourceReloader { + public static class Reloader implements SynchronousResourceReloader { @Override public void reload(ResourceManager manager) { GpuDevice device = RenderSystem.getDevice(); @@ -91,15 +91,4 @@ public void reload(ResourceManager manager) { } } } - - private static String read(String path) { - try { - return IOUtils.toString( - MinecraftClient.getInstance().getResourceManager() - .getResource(Identifier.of("pupper", "shaders/" + path)).get().getInputStream(), - StandardCharsets.UTF_8); - } catch (IOException e) { - throw new IllegalStateException("Could not read shader '" + path + "'", e); - } - } } diff --git a/src/main/java/cn/pupperclient/shader/VertexFormatElements.java b/src/main/java/cn/pupperclient/shader/VertexFormatElements.java new file mode 100644 index 0000000..d2690d7 --- /dev/null +++ b/src/main/java/cn/pupperclient/shader/VertexFormatElements.java @@ -0,0 +1,28 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package cn.pupperclient.shader; + +import com.mojang.blaze3d.vertex.VertexFormatElement; + +public abstract class VertexFormatElements { + public static final VertexFormatElement POS2 = VertexFormatElement.register(getNextVertexFormatElementId(), 0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.POSITION, 2); + + private VertexFormatElements() {} + + private static int getNextVertexFormatElementId() { + int id = 0; + + while (VertexFormatElement.byId(id) != null) { + id++; + + if (id >= 32) { + throw new RuntimeException("Too many mods registering VertexFormatElements"); + } + } + + return id; + } +} diff --git a/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java b/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java index 90aefb2..dd4a676 100644 --- a/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java +++ b/src/main/java/cn/pupperclient/shader/impl/Kawaseblur.java @@ -11,6 +11,7 @@ import com.mojang.blaze3d.vertex.VertexFormat; import it.unimi.dsi.fastutil.ints.IntDoubleImmutablePair; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.VertexFormats; import java.util.OptionalInt; @@ -49,13 +50,22 @@ public void draw(int radius) { } } - // 创建全屏四边形网格 - mesh = new MeshBuilder(PupperRenderPipelines.POS2_TEXTURE, VertexFormat.DrawMode.TRIANGLES); + // 创建全屏四边形网格,使用正确的顶点数据 + mesh = new MeshBuilder(VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLES); mesh.begin(); - mesh.vec2(-1, -1).next(); - mesh.vec2(-1, 1).next(); - mesh.vec2(1, 1).next(); - mesh.vec2(1, -1).next(); + + // 顶点1: (-1, -1, 0) 纹理坐标: (0, 0) + mesh.vec3(-1, -1, 0).vec2(0, 0).next(); + + // 顶点2: (-1, 1, 0) 纹理坐标: (0, 1) + mesh.vec3(-1, 1, 0).vec2(0, 1).next(); + + // 顶点3: (1, 1, 0) 纹理坐标: (1, 1) + mesh.vec3(1, 1, 0).vec2(1, 1).next(); + + // 顶点4: (1, -1, 0) 纹理坐标: (1, 0) + mesh.vec3(1, -1, 0).vec2(1, 0).next(); + mesh.quad(0, 1, 2, 3); mesh.end(); @@ -66,10 +76,7 @@ public void draw(int radius) { int iterations = strength.leftInt(); double offset = strength.rightDouble(); - GpuBuffer vertexBuffer = mesh.createVertexBuffer(); - GpuBuffer indexBuffer = mesh.createIndexBuffer(); - - try { + try (GpuBuffer vertexBuffer = mesh.createVertexBuffer(); GpuBuffer indexBuffer = mesh.createIndexBuffer()) { // 第一遍:降采样 renderToFbo(fbos[0], MinecraftClient.getInstance().getFramebuffer().getColorAttachment(), @@ -103,9 +110,6 @@ public void draw(int radius) { pass.drawIndexed(0, mesh.getIndicesCount()); pass.close(); - } finally { - vertexBuffer.close(); - indexBuffer.close(); } } diff --git a/src/main/resources/assets/pupper/shaders/blur.vert b/src/main/resources/assets/pupper/shaders/blur.vert index ab7813c..24684cb 100644 --- a/src/main/resources/assets/pupper/shaders/blur.vert +++ b/src/main/resources/assets/pupper/shaders/blur.vert @@ -2,10 +2,11 @@ precision lowp float; -layout (location = 0) in vec2 pos; +layout (location = 0) in vec3 Position; +layout (location = 1) in vec2 UV0; out vec2 uv; void main() { - gl_Position = vec4(pos, 0, 1); - uv = pos * .5 + .5; -} \ No newline at end of file + gl_Position = vec4(Position, 1.0); + uv = UV0; +} diff --git a/src/main/resources/assets/pupper/shaders/passthrough.vert b/src/main/resources/assets/pupper/shaders/passthrough.vert index f588849..83cb6a9 100644 --- a/src/main/resources/assets/pupper/shaders/passthrough.vert +++ b/src/main/resources/assets/pupper/shaders/passthrough.vert @@ -2,7 +2,8 @@ precision lowp float; -layout (location = 0) in vec2 pos; +layout (location = 0) in vec3 Position; +layout (location = 1) in vec2 UV0; out vec2 uv; void main() { From f30ffb735ef3387c7a39853bc17c7641768c5f93 Mon Sep 17 00:00:00 2001 From: oneachina Date: Sun, 11 Jan 2026 15:46:02 +0800 Subject: [PATCH 21/21] fix: asdasdasdsadasdsd --- .../gui/modmenu/pages/MusicPage.java | 2 +- .../client/MixinMinecraftClient.java | 2 +- .../minecraft/client/util/MixinWindow.java | 2 +- .../shader/PupperRenderPipelines.java | 40 +- .../shader/PupperVertexFormats.java | 28 + src/main/java/cn/pupperclient/skia/Skia.java | 78 ++- .../skia/context/SkiaContext.java | 581 ++-------------- .../pupperclient/skia/image/ImageHelper.java | 634 ++---------------- .../cn/pupperclient/utils/ImageUtils.java | 90 ++- .../resources/assets/pupper/shaders/blur.vert | 7 +- .../assets/pupper/shaders/blur_down.frag | 6 +- .../assets/pupper/shaders/blur_up.frag | 6 - .../assets/pupper/shaders/passthrough.vert | 3 +- src/main/resources/pupper.accesswidener | 2 + 14 files changed, 278 insertions(+), 1203 deletions(-) create mode 100644 src/main/java/cn/pupperclient/shader/PupperVertexFormats.java diff --git a/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java b/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java index d4fadd1..bd55c4b 100644 --- a/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java +++ b/src/main/java/cn/pupperclient/gui/modmenu/pages/MusicPage.java @@ -311,7 +311,7 @@ private void drawRoundedImage(File file, float x, float y, float width, float he Skia.drawImage(file, x, y, width, height); - if (Skia.getImageHelper().load(file) != null) { + if (Skia.getImageHelper().load(file)) { Image image = Skia.getImageHelper().get(file.getName()); if (image != null) { Skia.getCanvas().drawImageRect(image, Rect.makeWH(image.getWidth(), image.getHeight()), diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java index 595a7cf..4c8d3fc 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/MixinMinecraftClient.java @@ -137,7 +137,7 @@ public void init(CallbackInfo ci) throws IOException { int width = window.getWidth(); int height = window.getHeight(); if (width > 0 && height > 0) { - SkiaContext.createSimpleSurface(width, height); + SkiaContext.createSurface(width, height); } else { PupperClient.LOGGER.warn("Window size during init is invalid: {}x{}", width, height); } diff --git a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java index 47f219f..4dc2c7b 100644 --- a/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java +++ b/src/main/java/cn/pupperclient/mixin/mixins/minecraft/client/util/MixinWindow.java @@ -25,7 +25,7 @@ public class MixinWindow { @Inject(method = "onFramebufferSizeChanged", at = @At("RETURN")) private void onFramebufferSizeChanged(long window, int width, int height, CallbackInfo ci) { if (width > 0 && height > 0) { - SkiaContext.createSimpleSurface(width, height); + SkiaContext.createSurface(width, height); } else { PupperClient.LOGGER.warn("Window size is invalid: {}x{}, skipping surface creation", width, height); } diff --git a/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java b/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java index a132c10..8f3ecbb 100644 --- a/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java +++ b/src/main/java/cn/pupperclient/shader/PupperRenderPipelines.java @@ -7,8 +7,6 @@ import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.blaze3d.vertex.VertexFormatElement; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.UniformType; import net.minecraft.client.render.VertexFormats; import net.minecraft.resource.ResourceManager; @@ -24,21 +22,12 @@ public class PupperRenderPipelines { private static final List PIPELINES = new ArrayList<>(); - // 着色器代码缓存 - private static final Identifier BLUR_VERT = PupperClient.identifier("shaders/blur.vert"); - private static final Identifier BLUR_DOWN_FRAG = PupperClient.identifier("shaders/blur_down.frag"); - private static final Identifier BLUR_UP_FRAG = PupperClient.identifier("shaders/blur_up.frag"); - private static final Identifier PASSTHROUGH_VERT = PupperClient.identifier("shaders/passthrough.vert"); - private static final Identifier PASSTHROUGH_FRAG = PupperClient.identifier("shaders/passthrough.frag"); - - // 着色器管线定义 public static final RenderPipeline BLUR_DOWN = register(new RenderPipeline.Builder() - .withVertexShader(BLUR_VERT) - .withFragmentShader(BLUR_DOWN_FRAG) - .withVertexFormat(VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withLocation(PupperClient.identifier("pipeline/blur/up")) + .withVertexFormat(PupperVertexFormats.POS2, VertexFormat.DrawMode.TRIANGLES) + .withVertexShader(PupperClient.identifier("shaders/passthrough.vert")) + .withFragmentShader(PupperClient.identifier("shaders/passthrough.frag")) .withSampler("uTexture") - .withUniform("uHalfTexelSize", UniformType.VEC2) - .withUniform("uOffset", UniformType.FLOAT) .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) .withDepthWrite(false) .withBlend(BlendFunction.TRANSLUCENT) @@ -46,9 +35,10 @@ public class PupperRenderPipelines { .build()); public static final RenderPipeline BLUR_UP = register(new RenderPipeline.Builder() - .withVertexShader(BLUR_VERT) - .withFragmentShader(BLUR_UP_FRAG) - .withVertexFormat(VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withLocation(PupperClient.identifier("pipeline/blur/up")) + .withVertexFormat(PupperVertexFormats.POS2, VertexFormat.DrawMode.TRIANGLES) + .withVertexShader(PupperClient.identifier("shaders/blur.vert")) + .withFragmentShader(PupperClient.identifier("shaders/blur_up.frag")) .withSampler("uTexture") .withUniform("uHalfTexelSize", UniformType.VEC2) .withUniform("uOffset", UniformType.FLOAT) @@ -56,18 +46,21 @@ public class PupperRenderPipelines { .withDepthWrite(false) .withBlend(BlendFunction.TRANSLUCENT) .withCull(false) - .build()); + .build() + ); public static final RenderPipeline PASSTHROUGH = register(new RenderPipeline.Builder() - .withVertexShader(PASSTHROUGH_VERT) - .withFragmentShader(PASSTHROUGH_FRAG) - .withVertexFormat(VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLES) + .withLocation(PupperClient.identifier("pipeline/blur/up")) + .withVertexFormat(PupperVertexFormats.POS2, VertexFormat.DrawMode.TRIANGLES) + .withVertexShader(PupperClient.identifier("shaders/passthrough.vert")) + .withFragmentShader(PupperClient.identifier("shaders/passthrough.frag")) .withSampler("uTexture") .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) .withDepthWrite(false) .withBlend(BlendFunction.TRANSLUCENT) .withCull(false) - .build()); + .build() + ); private static RenderPipeline register(RenderPipeline pipeline) { PIPELINES.add(pipeline); @@ -82,6 +75,7 @@ public void reload(ResourceManager manager) { for (RenderPipeline pipeline : PIPELINES) { device.precompilePipeline(pipeline, (identifier, shaderType) -> { var resource = manager.getResource(identifier).get(); + try (var in = resource.getInputStream()) { return IOUtils.toString(in, StandardCharsets.UTF_8); } catch (IOException e) { diff --git a/src/main/java/cn/pupperclient/shader/PupperVertexFormats.java b/src/main/java/cn/pupperclient/shader/PupperVertexFormats.java new file mode 100644 index 0000000..fffac80 --- /dev/null +++ b/src/main/java/cn/pupperclient/shader/PupperVertexFormats.java @@ -0,0 +1,28 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package cn.pupperclient.shader; + +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; + +public abstract class PupperVertexFormats { + public static final VertexFormat POS2 = VertexFormat.builder() + .add("Position", VertexFormatElements.POS2) + .build(); + + public static final VertexFormat POS2_COLOR = VertexFormat.builder() + .add("Position", VertexFormatElements.POS2) + .add("Color", VertexFormatElement.COLOR) + .build(); + + public static final VertexFormat POS2_TEXTURE_COLOR = VertexFormat.builder() + .add("Position", VertexFormatElements.POS2) + .add("Texture", VertexFormatElement.UV) + .add("Color", VertexFormatElement.COLOR) + .build(); + + private PupperVertexFormats() {} +} diff --git a/src/main/java/cn/pupperclient/skia/Skia.java b/src/main/java/cn/pupperclient/skia/Skia.java index dd9eda3..223c2f3 100644 --- a/src/main/java/cn/pupperclient/skia/Skia.java +++ b/src/main/java/cn/pupperclient/skia/Skia.java @@ -2,13 +2,11 @@ import java.awt.Color; import java.io.File; -import java.util.Objects; import cn.pupperclient.management.mod.impl.settings.HUDModSettings; import cn.pupperclient.shader.impl.Kawaseblur; import cn.pupperclient.skia.context.SkiaContext; import cn.pupperclient.skia.image.ImageHelper; -import com.mojang.blaze3d.textures.GpuTexture; import io.github.humbleui.skija.Canvas; import io.github.humbleui.skija.ClipMode; import io.github.humbleui.skija.FilterTileMode; @@ -121,49 +119,49 @@ public static void drawImage(String path, float x, float y, float width, float h path = "/assets/pupper/" + path; - if (imageHelper.load(path) != null) { - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(path)), Rect.makeXYWH(x, y, width, height)); + if (imageHelper.load(path)) { + getCanvas().drawImageRect(imageHelper.get(path), Rect.makeXYWH(x, y, width, height)); } } - public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height, float alpha, + public static void drawImage(int textureId, float x, float y, float width, float height, float alpha, SurfaceOrigin origin) { - if (imageHelper.load(Gputexture, width, height) != null) { + if (imageHelper.load(textureId, width, height, origin)) { Paint paint = new Paint(); paint.setAlpha((int) (255 * alpha)); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(Gputexture)), Rect.makeXYWH(x, y, width, height), paint); + getCanvas().drawImageRect(imageHelper.get(textureId), Rect.makeXYWH(x, y, width, height), paint); } } - public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height, float alpha) { - drawImage(Gputexture, x, y, width, height, alpha, SurfaceOrigin.TOP_LEFT); + public static void drawImage(int textureId, float x, float y, float width, float height, float alpha) { + drawImage(textureId, x, y, width, height, alpha, SurfaceOrigin.TOP_LEFT); } public static void drawImage(File file, float x, float y, float width, float height) { - if (imageHelper.load(file) != null) { - getCanvas().drawImageRect(Objects.requireNonNull(Objects.requireNonNull(imageHelper.get(file.getName()))), Rect.makeXYWH(x, y, width, height)); + if (imageHelper.load(file)) { + getCanvas().drawImageRect(imageHelper.get(file.getName()), Rect.makeXYWH(x, y, width, height)); } } - public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height, SurfaceOrigin origin) { + public static void drawImage(int textureId, float x, float y, float width, float height, SurfaceOrigin origin) { - if (imageHelper.load(Gputexture, width, height) != null) { - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(Gputexture)), Rect.makeXYWH(x, y, width, height)); + if (imageHelper.load(textureId, width, height, origin)) { + getCanvas().drawImageRect(imageHelper.get(textureId), Rect.makeXYWH(x, y, width, height)); } } - public static void drawImage(GpuTexture Gputexture, float x, float y, float width, float height) { - drawImage(Gputexture, x, y, width, height, SurfaceOrigin.TOP_LEFT); + public static void drawImage(int textureId, float x, float y, float width, float height) { + drawImage(textureId, x, y, width, height, SurfaceOrigin.TOP_LEFT); } - public static void drawRoundedImage(GpuTexture Gputexture, float x, float y, float width, float height, float radius) { + public static void drawRoundedImage(int textureId, float x, float y, float width, float height, float radius) { Path path = Path.makeRRect(RRect.makeXYWH(x, y, width, height, radius)); save(); getCanvas().clipPath(path, ClipMode.INTERSECT, true); - drawImage(Gputexture, x, y, width, height); + drawImage(textureId, x, y, width, height); restore(); } @@ -187,23 +185,23 @@ public static void drawRoundedImage(File file, float x, float y, float width, fl restore(); } - public static void drawRoundedImage(GpuTexture Gputexture, float x, float y, float width, float height, float radius, + public static void drawRoundedImage(int textureId, float x, float y, float width, float height, float radius, float alpha, SurfaceOrigin origin) { Path path = Path.makeRRect(RRect.makeXYWH(x, y, width, height, radius)); save(); getCanvas().clipPath(path, ClipMode.INTERSECT, true); - drawImage(Gputexture, x, y, width, height, alpha, origin); + drawImage(textureId, x, y, width, height, alpha, origin); restore(); } - public static void drawRoundedImage(GpuTexture Gputexture, float x, float y, float width, float height, float radius, + public static void drawRoundedImage(int textureId, float x, float y, float width, float height, float radius, float alpha) { - drawRoundedImage(Gputexture, x, y, width, height, radius, alpha, SurfaceOrigin.TOP_LEFT); + drawRoundedImage(textureId, x, y, width, height, radius, alpha, SurfaceOrigin.TOP_LEFT); } public static void drawPlayerHead(File file, float x, float y, float width, float height, float radius) { - if (imageHelper.load(file) != null) { + if (imageHelper.load(file)) { Path path = Path.makeRRect(RRect.makeXYWH(x, y, width, height, radius)); @@ -213,14 +211,14 @@ public static void drawPlayerHead(File file, float x, float y, float width, floa save(); getCanvas().clipPath(path, ClipMode.INTERSECT, true); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), srcRect, dstRect, null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), srcRect1, dstRect, null, false); + getCanvas().drawImageRect(imageHelper.get(file.getName()), srcRect, dstRect, null, false); + getCanvas().drawImageRect(imageHelper.get(file.getName()), srcRect1, dstRect, null, false); restore(); } } public static void drawSkin(File file, float x, float y, float scale) { - if (imageHelper.load(file) != null) { + if (imageHelper.load(file)) { Rect head = Rect.makeXYWH(8, 8, 8, 8); Rect headLayer = Rect.makeXYWH(40, 8, 8, 8); @@ -237,42 +235,42 @@ public static void drawSkin(File file, float x, float y, float scale) { save(); scale(x, y, scale); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), head, + getCanvas().drawImageRect(imageHelper.get(file.getName()), head, Rect.makeXYWH(x + leftArm.getWidth(), y, head.getWidth(), head.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), headLayer, + getCanvas().drawImageRect(imageHelper.get(file.getName()), headLayer, Rect.makeXYWH(x + leftArm.getWidth(), y, headLayer.getWidth(), headLayer.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), body, + getCanvas().drawImageRect(imageHelper.get(file.getName()), body, Rect.makeXYWH(x + leftArm.getWidth(), y + head.getHeight(), body.getWidth(), body.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), bodyLayer, Rect.makeXYWH(x + leftArm.getWidth(), + getCanvas().drawImageRect(imageHelper.get(file.getName()), bodyLayer, Rect.makeXYWH(x + leftArm.getWidth(), y + headLayer.getHeight(), bodyLayer.getWidth(), bodyLayer.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), leftArm, + getCanvas().drawImageRect(imageHelper.get(file.getName()), leftArm, Rect.makeXYWH(x, y + head.getHeight(), leftArm.getWidth(), leftArm.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), leftArmLayer, + getCanvas().drawImageRect(imageHelper.get(file.getName()), leftArmLayer, Rect.makeXYWH(x, y + headLayer.getHeight(), leftArmLayer.getWidth(), leftArmLayer.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), rightArm, + getCanvas().drawImageRect(imageHelper.get(file.getName()), rightArm, Rect.makeXYWH(x + leftArm.getWidth() + body.getWidth(), y + head.getHeight(), rightArm.getWidth(), rightArm.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(Objects.requireNonNull(imageHelper.get(file.getName()))), rightArmLayer, + getCanvas().drawImageRect(imageHelper.get(file.getName()), rightArmLayer, Rect.makeXYWH(x + leftArmLayer.getWidth() + bodyLayer.getWidth(), y + headLayer.getHeight(), rightArmLayer.getWidth(), rightArmLayer.getHeight()), null, false); getCanvas().drawImageRect( - Objects.requireNonNull(imageHelper.get(file.getName())), leftLeg, Rect.makeXYWH(x + leftArm.getWidth(), + imageHelper.get(file.getName()), leftLeg, Rect.makeXYWH(x + leftArm.getWidth(), y + head.getHeight() + body.getHeight(), leftLeg.getWidth(), leftLeg.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), leftLegLayer, + getCanvas().drawImageRect(imageHelper.get(file.getName()), leftLegLayer, Rect.makeXYWH(x + leftArmLayer.getWidth(), y + headLayer.getHeight() + bodyLayer.getHeight(), leftLegLayer.getWidth(), leftLegLayer.getHeight()), null, false); getCanvas() - .drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), rightLeg, + .drawImageRect(imageHelper.get(file.getName()), rightLeg, Rect.makeXYWH(x + leftArm.getWidth() + leftLeg.getWidth(), y + head.getHeight() + body.getHeight(), rightLeg.getWidth(), rightLeg.getHeight()), null, false); - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(file.getName())), rightLegLayer, + getCanvas().drawImageRect(imageHelper.get(file.getName()), rightLegLayer, Rect.makeXYWH(x + leftArmLayer.getWidth() + leftLegLayer.getWidth(), y + headLayer.getHeight() + bodyLayer.getHeight(), rightLegLayer.getWidth(), rightLegLayer.getHeight()), @@ -284,8 +282,8 @@ public static void drawSkin(File file, float x, float y, float scale) { public static void drawMinecraftImage(String path, float x, float y, float width, float height) { Identifier identifier = Identifier.of("minecraft", path); - if (imageHelper.load(identifier) != null) { - getCanvas().drawImageRect(Objects.requireNonNull(imageHelper.get(identifier.getPath())), Rect.makeXYWH(x, y, width, height)); + if (imageHelper.load(identifier)) { + getCanvas().drawImageRect(imageHelper.get(identifier.getPath()), Rect.makeXYWH(x, y, width, height)); } } diff --git a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java index 076ddae..52f19d8 100644 --- a/src/main/java/cn/pupperclient/skia/context/SkiaContext.java +++ b/src/main/java/cn/pupperclient/skia/context/SkiaContext.java @@ -1,291 +1,36 @@ package cn.pupperclient.skia.context; -import cn.pupperclient.PupperLogger; -import cn.pupperclient.skia.image.ImageHelper; -import cn.pupperclient.utils.Unstable; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.GpuTexture; -import io.github.humbleui.skija.*; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.texture.NativeImage; -import org.jetbrains.annotations.Nullable; - -import java.nio.ByteBuffer; import java.util.function.Consumer; -@Environment(EnvType.CLIENT) -public class SkiaContext { - - private static DirectContext directContext = null; - private static Surface skiaSurface = null; - private static BackendRenderTarget renderTarget = null; - - private static GpuTexture currentTexture = null; - private static NativeImage currentNativeImage = null; - - private static int surfaceWidth = 0; - private static int surfaceHeight = 0; - - /** - * 使用 GpuTexture 创建 Skia 表面 - */ - public static void createSurfaceFromGpuTexture(GpuTexture texture) { - if (texture == null) { - throw new IllegalArgumentException("GpuTexture cannot be null"); - } - - // 清理旧资源 - cleanup(); - - // 保存当前纹理 - currentTexture = texture; - surfaceWidth = texture.getWidth(0); - surfaceHeight = texture.getHeight(0); - - // 创建 Skia 上下文(如果需要) - if (directContext == null) { - directContext = DirectContext.makeGL(); - } - - try { - // 方法1:尝试创建 GPU 表面 - createGpuSurface(surfaceWidth, surfaceHeight); - } catch (Exception e) { - System.err.println("Failed to create GPU surface, falling back to CPU surface: " + e.getMessage()); - // 方法2:回退到 CPU 表面 - createCpuSurface(surfaceWidth, surfaceHeight); - } - - // 从 GpuTexture 加载数据到表面 - loadTextureToSurface(); - } - - /** - * 创建 GPU 表面(使用 OpenGL 纹理) - */ - private static void createGpuSurface(int width, int height) { - if (width <= 0 || height <= 0) { - PupperLogger.warn("SkiaContext", "Attempted to create GPU surface with invalid dimensions: " + width + "x" + height); - return; - } - - // 生成一个临时的 OpenGL 纹理供 Skia 使用 - int glTextureId = generateGlTexture(width, height); - - // 创建 Skia 渲染目标 - renderTarget = BackendRenderTarget.makeGL( - width, - height, - 0, // 采样数 - 8, // 模板位数 - glTextureId, - org.lwjgl.opengl.GL11.GL_RGBA8 - ); - - // 创建 Skia 表面 - skiaSurface = Surface.wrapBackendRenderTarget( - directContext, - renderTarget, - SurfaceOrigin.BOTTOM_LEFT, - SurfaceColorFormat.RGBA_8888, - ColorSpace.getSRGB() - ); - } - - /** - * 创建 CPU 表面(纯软件渲染) - */ - private static void createCpuSurface(int width, int height) { - if (width <= 0 || height <= 0) { - PupperLogger.warn("SkiaContext", "Attempted to create CPU surface with invalid dimensions: " + width + "x" + height); - return; - } - - try { - ImageInfo imageInfo = ImageInfo.makeS32(width, height, ColorAlphaType.PREMUL); - skiaSurface = Surface.makeRaster(imageInfo); - } catch (Exception e) { - PupperLogger.error("SkiaContext", "Failed to create CPU surface: " + e); - } - } - - /** - * 生成临时的 OpenGL 纹理 - */ - private static int generateGlTexture(int width, int height) { - // 使用 LWJGL 直接创建 OpenGL 纹理 - int[] textures = new int[1]; - org.lwjgl.opengl.GL11.glGenTextures(textures); - int textureId = textures[0]; - - org.lwjgl.opengl.GL11.glBindTexture(org.lwjgl.opengl.GL11.GL_TEXTURE_2D, textureId); - - // 设置纹理参数 - org.lwjgl.opengl.GL11.glTexParameteri( - org.lwjgl.opengl.GL11.GL_TEXTURE_2D, - org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER, - org.lwjgl.opengl.GL11.GL_LINEAR - ); - org.lwjgl.opengl.GL11.glTexParameteri( - org.lwjgl.opengl.GL11.GL_TEXTURE_2D, - org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER, - org.lwjgl.opengl.GL11.GL_LINEAR - ); - - // 分配纹理存储 - org.lwjgl.opengl.GL11.glTexImage2D( - org.lwjgl.opengl.GL11.GL_TEXTURE_2D, - 0, - org.lwjgl.opengl.GL11.GL_RGBA8, - width, - height, - 0, - org.lwjgl.opengl.GL11.GL_RGBA, - org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE, - (ByteBuffer) null - ); - - org.lwjgl.opengl.GL11.glBindTexture(org.lwjgl.opengl.GL11.GL_TEXTURE_2D, 0); - - return textureId; - } - - /** - * 从 GpuTexture 加载数据到 Skia 表面 - */ - private static void loadTextureToSurface() { - if (currentTexture == null || skiaSurface == null) { - return; - } - - try { - // 读取 GpuTexture 数据到 ByteBuffer - ByteBuffer pixelData = ImageHelper.readGpuTextureDataCorrect(currentTexture); - if (pixelData == null) { - return; - } - - // 创建 Skia Image - ImageInfo imageInfo = ImageInfo.makeS32(surfaceWidth, surfaceHeight, ColorAlphaType.UNPREMUL); - Data data = Data.makeFromBytes(pixelData.array()); - - try (Image sourceImage = Image.makeRasterFromData(imageInfo, data, imageInfo.getMinRowBytes())) { - // 清除表面 - skiaSurface.getCanvas().clear(Color.makeARGB(0, 255, 255, 255)); //TRANSPARENT - - // 绘制到表面 - skiaSurface.getCanvas().drawImage(sourceImage, 0, 0); - - // 刷新 - if (directContext != null) { - directContext.flush(); - } - } - - } catch (Exception e) { - PupperLogger.error("SkiaContext", "loadTextureToSurface error:" + e); - } - } - - /** - * 执行绘制逻辑 - */ - public static void draw(Consumer drawingLogic) { - if (skiaSurface == null) { - PupperLogger.warn("SkiaContext", "Skia surface not initialized. Drawing skipped."); - return; - } - - saveGlState(); - - try { - // 获取画布 - Canvas canvas = getCanvas(); - if (canvas == null) { - return; - } - - // 执行用户绘制逻辑 - drawingLogic.accept(canvas); - - // 刷新上下文 - if (directContext != null) { - directContext.flush(); - } - - // 将结果保存回 GpuTexture - saveSurfaceToTexture(); - - } finally { - // 恢复 OpenGL 状态 - restoreGlState(); - } - } - - /** - * 将 Skia 表面内容保存回 GpuTexture - */ - private static void saveSurfaceToTexture() { - if (skiaSurface == null || currentTexture == null) { - return; - } - - try { - // 获取表面快照 - - try (Image snapshot = skiaSurface.makeImageSnapshot()) { - // 将 Skia Image 转换为 NativeImage - - try (NativeImage nativeImage = convertSkiaImageToNativeImage(snapshot)) { - // 使用 CommandEncoder 写入 GpuTexture - var device = RenderSystem.getDevice(); - var encoder = device.createCommandEncoder(); - - encoder.writeToTexture(currentTexture, nativeImage); +import com.mojang.blaze3d.opengl.GlConst; +import com.mojang.blaze3d.opengl.GlStateManager; +import io.github.humbleui.skija.*; +import net.minecraft.client.gl.Framebuffer; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL33; - } - } +import com.mojang.blaze3d.systems.RenderSystem; - } catch (Exception e) { - PupperLogger.error("SkiaContext", "saveSurfaceToTexture error:" + e); - } - } +public class SkiaContext { - /** - * 将 Skia Image 转换为 NativeImage - */ - @Unstable - private static NativeImage convertSkiaImageToNativeImage(Image skiaImage) { - return convertToNativeImage(skiaImage); - } + private static DirectContext context = null; + private static Surface surface; + private static BackendRenderTarget renderTarget; - /** - * 获取画布 - */ - @Nullable public static Canvas getCanvas() { - return skiaSurface != null ? skiaSurface.getCanvas() : null; + return surface.getCanvas(); } - /** - * 获取 DirectContext - */ - public static DirectContext getContext() { - if (directContext == null) { - directContext = DirectContext.makeGL(); + public static void createSurface(int width, int height) { + + if (context == null) { + context = DirectContext.makeGL(); } - return directContext; - } - /** - * 清理资源 - */ - public static void cleanup() { - if (skiaSurface != null) { - skiaSurface.close(); - skiaSurface = null; + if (surface != null) { + surface.close(); + surface = null; } if (renderTarget != null) { @@ -293,258 +38,48 @@ public static void cleanup() { renderTarget = null; } - if (currentNativeImage != null) { - currentNativeImage.close(); - currentNativeImage = null; - } - - currentTexture = null; - surfaceWidth = 0; - surfaceHeight = 0; - } - - /** - * 完全关闭(释放所有资源) - */ - public static void close() { - cleanup(); - - if (directContext != null) { - directContext.close(); - directContext = null; - } - } - - /** - * 保存当前 OpenGL 状态 - */ - private static void saveGlState() { - // 使用直接 OpenGL 调用保存状态 - // 注意:1.21.5 移除了很多 RenderSystem 方法 - int[] prevTexture = new int[1]; - org.lwjgl.opengl.GL11.glGetIntegerv(org.lwjgl.opengl.GL11.GL_TEXTURE_BINDING_2D, prevTexture); - - // 保存混合状态 - int[] blendEnabled = new int[1]; - org.lwjgl.opengl.GL11.glGetIntegerv(org.lwjgl.opengl.GL11.GL_BLEND, blendEnabled); - - // 保存视口 - int[] viewport = new int[4]; - org.lwjgl.opengl.GL11.glGetIntegerv(org.lwjgl.opengl.GL11.GL_VIEWPORT, viewport); - - // 存储到线程局部变量 - GlStateSavedState savedState = new GlStateSavedState(); - savedState.textureBinding = prevTexture[0]; - savedState.blendEnabled = blendEnabled[0] == org.lwjgl.opengl.GL11.GL_TRUE; - savedState.viewportX = viewport[0]; - savedState.viewportY = viewport[1]; - savedState.viewportWidth = viewport[2]; - savedState.viewportHeight = viewport[3]; - - ThreadLocalState.setState(savedState); - } - - /** - * 恢复 OpenGL 状态 - */ - private static void restoreGlState() { - GlStateSavedState savedState = ThreadLocalState.getState(); - if (savedState == null) { - return; - } - - // 恢复纹理绑定 - org.lwjgl.opengl.GL11.glBindTexture(org.lwjgl.opengl.GL11.GL_TEXTURE_2D, savedState.textureBinding); - - // 恢复混合状态 - if (savedState.blendEnabled) { - org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL11.GL_BLEND); - } else { - org.lwjgl.opengl.GL11.glDisable(org.lwjgl.opengl.GL11.GL_BLEND); - } - - // 恢复视口 - org.lwjgl.opengl.GL11.glViewport( - savedState.viewportX, - savedState.viewportY, - savedState.viewportWidth, - savedState.viewportHeight - ); - } - - /** - * 创建一个简单的表面(用于独立的 Skia 绘制) - */ - public static void createSimpleSurface(int width, int height) { - if (width <= 0 || height <= 0) { - PupperLogger.warn("SkiaContext", "Attempted to create simple surface with invalid dimensions: " + width + "x" + height); - cleanup(); // 清理旧资源 - return; - } - - cleanup(); - - surfaceWidth = width; - surfaceHeight = height; - - // 创建 CPU 表面 - createCpuSurface(width, height); - } - - /** - * 获取表面尺寸 - */ - public static int[] getSurfaceSize() { - return new int[]{surfaceWidth, surfaceHeight}; - } - - /** - * 检查表面是否已创建 - */ - public static boolean isSurfaceCreated() { - return skiaSurface != null; - } - - /** - * 获取当前使用的纹理 - */ - @Nullable - public static GpuTexture getCurrentTexture() { - return currentTexture; + renderTarget = BackendRenderTarget.makeGL(width, height, 0, 8, + Framebuffer.index, GL11.GL_RGBA8); + surface = Surface.wrapBackendRenderTarget(context, renderTarget, SurfaceOrigin.BOTTOM_LEFT, + SurfaceColorFormat.BGRA_8888, ColorSpace.getSRGB()); } - /** - * 从 Minecraft 的帧缓冲创建表面 - */ - public static void createSurfaceFromFramebuffer() { - var framebuffer = MinecraftClient.getInstance().getFramebuffer(); - GpuTexture colorTexture = framebuffer.getColorAttachment(); - - if (colorTexture == null) { - throw new IllegalStateException("Framebuffer has no color attachment"); - } - - createSurfaceFromGpuTexture(colorTexture); - } - - // 线程局部状态保存 - private static class ThreadLocalState { - private static final ThreadLocal STATE = new ThreadLocal<>(); - - public static void setState(GlStateSavedState state) { - STATE.set(state); - } - - public static GlStateSavedState getState() { - return STATE.get(); - } - - public static void clear() { - STATE.remove(); - } - } - - // OpenGL 状态保存结构 - private static class GlStateSavedState { - int textureBinding = 0; - boolean blendEnabled = false; - int viewportX = 0; - int viewportY = 0; - int viewportWidth = 0; - int viewportHeight = 0; - } - - private static NativeImage convertToNativeImage(Image skiaImage) { - int width = skiaImage.getWidth(); - int height = skiaImage.getHeight(); - - try { - // 创建 Bitmap - Bitmap bitmap = new Bitmap(); - ImageInfo bitmapInfo = ImageInfo.makeS32(width, height, ColorAlphaType.UNPREMUL); - - if (!bitmap.allocPixels(bitmapInfo)) { - PupperLogger.error("convertToNativeImage","无法分配 Bitmap 像素"); - return null; - } - - // 读取像素 - if (!skiaImage.readPixels(bitmap, 0, 0)) { - PupperLogger.error("convertToNativeImage","无法读取像素到 Bitmap"); - bitmap.close(); - return null; - } - - // 创建 NativeImage - NativeImage nativeImage = new NativeImage( - net.minecraft.client.texture.NativeImage.Format.RGBA, - width, - height, - false - ); - - // 获取 Bitmap 的像素数据 - ByteBuffer pixelBuffer = bitmap.peekPixels(); - if (pixelBuffer == null) { - PupperLogger.error("convertToNativeImage","无法获取 Bitmap 像素缓冲区"); - bitmap.close(); - nativeImage.close(); - return null; - } - - // 批量转换像素(更高效) - convertPixelBufferBatch(pixelBuffer, nativeImage, width, height); - - bitmap.close(); - return nativeImage; + public static void draw(Consumer drawingLogic) { - } catch (Exception e) { - PupperLogger.error("convertToNativeImage","error: " + e); - return null; - } + GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0); + GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4); + GL11.glClearColor(0f, 0f, 0f, 0f); + context.resetGLAll(); + + Canvas canvas = getCanvas(); + drawingLogic.accept(canvas); + + context.flush(); + + GlStateManager._glBindVertexArray(0); + + GL33.glBindSampler(0, 0); + GlStateManager._disableBlend(); + GL11.glDisable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); + GL33.glBlendEquation(GL33.GL_FUNC_ADD); + GlStateManager._colorMask(true, true, true, true); + GL11.glColorMask(true, true, true, true); + GlStateManager._depthMask(true); + GL11.glDepthMask(true); + RenderSystem.disableScissor(); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + GL11.glDisable(GL11.GL_STENCIL_TEST); + GlStateManager._disableDepthTest(); + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL13.glActiveTexture(GL13.GL_TEXTURE0); + GlStateManager._activeTexture(GL13.GL_TEXTURE0); + GlStateManager._disableCull(); } - private static void convertPixelBufferBatch( - ByteBuffer pixelBuffer, - NativeImage nativeImage, - int width, - int height) { - - // 重置缓冲区位置 - pixelBuffer.rewind(); - - // 直接操作底层字节数组(如果可能) - if (pixelBuffer.hasArray()) { - byte[] pixelArray = pixelBuffer.array(); - int arrayOffset = pixelBuffer.arrayOffset(); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int baseIndex = (y * width + x) * 4 + arrayOffset; - - int r = pixelArray[baseIndex] & 0xFF; - int g = pixelArray[baseIndex + 1] & 0xFF; - int b = pixelArray[baseIndex + 2] & 0xFF; - int a = pixelArray[baseIndex + 3] & 0xFF; - - int nativeColor = (a << 24) | (b << 16) | (g << 8) | r; - nativeImage.setColor(x, y, nativeColor); - } - } - } else { - // 如果没有数组,使用 ByteBuffer 操作 - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int r = pixelBuffer.get() & 0xFF; - int g = pixelBuffer.get() & 0xFF; - int b = pixelBuffer.get() & 0xFF; - int a = pixelBuffer.get() & 0xFF; - - int nativeColor = (a << 24) | (b << 16) | (g << 8) | r; - nativeImage.setColor(x, y, nativeColor); - } - } - } + public static DirectContext getContext() { + return context; } - } diff --git a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java index 6f6da12..a403ae4 100644 --- a/src/main/java/cn/pupperclient/skia/image/ImageHelper.java +++ b/src/main/java/cn/pupperclient/skia/image/ImageHelper.java @@ -1,620 +1,120 @@ package cn.pupperclient.skia.image; -import cn.pupperclient.PupperLogger; +import cn.pupperclient.skia.context.SkiaContext; import cn.pupperclient.skia.utils.SkiaUtils; -import cn.pupperclient.utils.Unstable; -import com.mojang.blaze3d.buffers.BufferType; -import com.mojang.blaze3d.buffers.BufferUsage; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.GpuTexture; -import io.github.humbleui.skija.*; +import cn.pupperclient.utils.ImageUtils; +import io.github.humbleui.skija.ColorType; +import io.github.humbleui.skija.Image; +import io.github.humbleui.skija.SurfaceOrigin; import net.minecraft.client.MinecraftClient; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; import java.io.*; -import java.nio.ByteBuffer; +import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; public class ImageHelper { - private static final ExecutorService LOADER_EXECUTOR = Executors.newFixedThreadPool(2); - // 缓存系统 - private final Map fileImages = new ConcurrentHashMap<>(); - private final Map resourceImages = new ConcurrentHashMap<>(); - private final Map gpuTextureImages = new ConcurrentHashMap<>(); - private final Map> loadingFutures = new ConcurrentHashMap<>(); + private final Map images = new HashMap<>(); + private final Map textures = new HashMap<>(); - // 回调系统 - private final Map> callbacks = new ConcurrentHashMap<>(); + public boolean load(int texture, float width, float height, SurfaceOrigin origin) { - /** - * 从 GpuTexture 加载图像 - */ - public CompletableFuture loadAsync(GpuTexture texture) { - if (texture == null) { - return CompletableFuture.completedFuture(null); + if (!textures.containsKey(texture)) { + Image image = Image.adoptGLTextureFrom(SkiaContext.getContext(), texture, GL11.GL_TEXTURE_2D, (int) width, + (int) height, GL11.GL_RGBA8, origin, ColorType.RGBA_8888); + textures.put(texture, image); } - // 检查缓存 - if (gpuTextureImages.containsKey(texture)) { - return CompletableFuture.completedFuture(gpuTextureImages.get(texture)); - } - - String key = "gpu:" + texture.hashCode(); - if (loadingFutures.containsKey(key)) { - return loadingFutures.get(key); - } - - CompletableFuture future = CompletableFuture.supplyAsync(() -> { - try { - // 读取 GpuTexture 数据 - ByteBuffer pixelData = readGpuTextureData(texture); - if (pixelData == null) { - return null; - } - - // 创建 Skia Image - ImageInfo info = ImageInfo.makeS32( - texture.getWidth(0), - texture.getHeight(0), - ColorAlphaType.UNPREMUL - ); - - Data data = Data.makeFromBytes(pixelData.array()); - Image image = Image.makeRasterFromData(info, data, info.getMinRowBytes()); - - // 缓存结果 - gpuTextureImages.put(texture, image); - return image; - - } catch (Exception e) { - PupperLogger.error("ImageHelper/loadAsync(GpuTexture)", "CompletableFuture error:" + e); - return null; - } - }, LOADER_EXECUTOR); - - loadingFutures.put(key, future); - future.whenComplete((image, error) -> loadingFutures.remove(key)); - - return future; - } - - /** - * 同步从 GpuTexture 加载图像 - */ - @Nullable - @Unstable(reason = "float will be coerced to int") - public Image load(GpuTexture texture, float width, float height) { - if (texture == null) { - return null; - } - - // 检查缓存 - if (gpuTextureImages.containsKey(texture)) { - return gpuTextureImages.get(texture); - } - - try { - // 必须在渲染线程执行 - RenderSystem.assertOnRenderThread(); - - // 读取 GpuTexture 数据 - ByteBuffer pixelData = readGpuTextureData(texture); - if (pixelData == null) { - return null; - } - - // 创建 Skia Image - ImageInfo info = ImageInfo.makeS32( - (int) width, - (int) height, - ColorAlphaType.UNPREMUL - ); - - Data data = Data.makeFromBytes(pixelData.array()); - Image image = Image.makeRasterFromData(info, data, info.getMinRowBytes()); - - // 缓存结果 - gpuTextureImages.put(texture, image); - return image; - - } catch (Exception e) { - PupperLogger.error("ImageHelper/load(GpuTexture)", " error:" + e); - return null; - } - } - - /** - * 从文件路径异步加载图像 - */ - public CompletableFuture loadAsync(String filePath) { - if (filePath == null || filePath.isEmpty()) { - return CompletableFuture.completedFuture(null); - } - - // 检查缓存 - if (fileImages.containsKey(filePath)) { - return CompletableFuture.completedFuture(fileImages.get(filePath)); - } - - if (loadingFutures.containsKey(filePath)) { - return loadingFutures.get(filePath); - } - - CompletableFuture future = CompletableFuture.supplyAsync(() -> { - try { - Optional encodedBytes = SkiaUtils.convertToBytes(filePath); - if (encodedBytes.isEmpty()) { - return null; - } - - Image image = Image.makeDeferredFromEncodedBytes(encodedBytes.get()); - fileImages.put(filePath, image); - - return image; - - } catch (Exception e) { - PupperLogger.error("ImageHelper/loadAsync(filePath)", "loadAsync CompletableFuture error:" + e); - return null; - } - }, LOADER_EXECUTOR); - - loadingFutures.put(filePath, future); - future.whenComplete((image, error) -> { - loadingFutures.remove(filePath); - if (image != null && callbacks.containsKey(filePath)) { - callbacks.get(filePath).accept(image); - callbacks.remove(filePath); - } - }); - - return future; - } - - /** - * 从文件异步加载图像 - */ - public CompletableFuture loadAsync(File file) { - if (file == null || !file.exists()) { - return CompletableFuture.completedFuture(null); - } - - String filePath = file.getAbsolutePath(); - return loadAsync(filePath); + return true; } - /** - * 从资源标识符异步加载图像 - */ - public CompletableFuture loadAsync(Identifier identifier) { - if (identifier == null) { - return CompletableFuture.completedFuture(null); - } - - // 检查缓存 - if (resourceImages.containsKey(identifier)) { - return CompletableFuture.completedFuture(resourceImages.get(identifier)); - } - - String key = "resource:" + identifier.toString(); - if (loadingFutures.containsKey(key)) { - return loadingFutures.get(key); - } + public boolean load(Identifier identifier) { - CompletableFuture future = CompletableFuture.supplyAsync(() -> { + if (!images.containsKey(identifier.getPath())) { + ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager(); + Resource resource; try { - ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager(); - Resource resource = resourceManager.getResource(identifier).orElse(null); - if (resource == null) { - return null; - } - + resource = resourceManager.getResourceOrThrow(identifier); try (InputStream inputStream = resource.getInputStream()) { + byte[] imageData = inputStream.readAllBytes(); Image image = Image.makeDeferredFromEncodedBytes(imageData); - - resourceImages.put(identifier, image); - - return image; + images.put(identifier.getPath(), image); + return true; + } catch (IOException e) { + e.printStackTrace(); } - - } catch (IOException e) { - PupperLogger.error("ImageHelper/loadAsync(identifier)", "error:" + e); - return null; - } - }, LOADER_EXECUTOR); - - loadingFutures.put(key, future); - future.whenComplete((image, error) -> loadingFutures.remove(key)); - - return future; - } - - /** - * 同步加载文件 - */ - @Nullable - public Image load(String filePath) { - try { - Optional encodedBytes = SkiaUtils.convertToBytes(filePath); - if (encodedBytes.isEmpty()) { - return null; - } - - Image image = Image.makeDeferredFromEncodedBytes(encodedBytes.get()); - fileImages.put(filePath, image); - - return image; - - } catch (Exception e) { - PupperLogger.error("ImageHelper/load(filePath)", "error:" + e); - return null; - } - } - - /** - * 同步加载文件 - */ - @Nullable - public Image load(File file) { - if (file == null || !file.exists()) { - return null; - } - - try (FileInputStream fis = new FileInputStream(file)) { - byte[] encoded = org.apache.commons.io.IOUtils.toByteArray(fis); - Image image = Image.makeDeferredFromEncodedBytes(encoded); - - fileImages.put(file.getAbsolutePath(), image); - - return image; - - } catch (IOException e) { - PupperLogger.error("ImageHelper/load(file)", "error:" + e); - return null; - } - } - - /** - * 同步加载资源 - */ - @Nullable - public Image load(Identifier identifier) { - if (identifier == null) { - return null; - } - - // 检查缓存 - if (resourceImages.containsKey(identifier)) { - return resourceImages.get(identifier); - } - - try { - ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager(); - Resource resource = resourceManager.getResource(identifier).orElse(null); - if (resource == null) { - return null; + } catch (FileNotFoundException e) { + e.printStackTrace(); } - try (InputStream inputStream = resource.getInputStream()) { - byte[] imageData = inputStream.readAllBytes(); - Image image = Image.makeDeferredFromEncodedBytes(imageData); - - resourceImages.put(identifier, image); - - return image; - } - } catch (IOException e) { - PupperLogger.error("ImageHelper/load(identifier)", "error:" + e); - return null; } + return true; } - /** - * 添加加载完成回调 - */ - public void addCallback(String key, Consumer callback) { - callbacks.put(key, callback); - } - - /** - * 获取缓存的图像 - */ - @Nullable - public Image get(String key) { - return fileImages.get(key); - } - - @Nullable - public Image get(Identifier identifier) { - return resourceImages.get(identifier); - } - - @Nullable - public Image get(GpuTexture texture) { - return gpuTextureImages.get(texture); - } - - public static ByteBuffer readGpuTextureData(GpuTexture texture) { - if (texture == null) { - return null; - } - - RenderSystem.assertOnRenderThread(); - - try { - int width = texture.getWidth(0); - int height = texture.getHeight(0); - int bufferSize = width * height * 4; // RGBA8 - - // 创建用于存储读取结果的引用 - AtomicReference result = new AtomicReference<>(); - CountDownLatch latch = new CountDownLatch(1); - - // 创建 GPU 缓冲区用于读取 - - try (GpuBuffer readBuffer = RenderSystem.getDevice().createBuffer( - () -> "TextureReadBuffer", - BufferType.PIXEL_PACK, - BufferUsage.STREAM_READ, - bufferSize - )) { - // 创建命令编码器 - var commandEncoder = RenderSystem.getDevice().createCommandEncoder(); - - // 复制纹理数据到缓冲区,并设置回调 - commandEncoder.copyTextureToBuffer( - texture, - readBuffer, - 0, - () -> { - // 数据已上传到缓冲区的回调 - try { - // 在回调中读取缓冲区数据 - try (GpuBuffer.ReadView readView = commandEncoder.readBuffer(readBuffer)) { - ByteBuffer pixelData = BufferUtils.createByteBuffer(bufferSize); - ByteBuffer sourceBuffer = readView.data(); - sourceBuffer.rewind(); - pixelData.put(sourceBuffer); - pixelData.flip(); - - result.set(pixelData); - } - } catch (Exception e) { - PupperLogger.error("ImageHelper/readGpuTextureData", "copyTextureToBuffer error:" + e); - } finally { - latch.countDown(); - } - }, - 0 // mipLevel - ); - - // 等待数据读取完成 - boolean success = latch.await(5, TimeUnit.SECONDS); - if (!success) { - PupperLogger.error("ImageHelper/readGpuTextureData", "读取纹理数据超时"); - return null; + public boolean load(String filePath) { + if (!images.containsKey(filePath)) { + Optional encodedBytes = SkiaUtils.convertToBytes(filePath); + if (encodedBytes.isPresent()) { + Image image; + try { + image = Image.makeDeferredFromEncodedBytes(ImageUtils.convertToPng(encodedBytes.get())); + } catch (IOException e) { + return false; } - - return result.get(); - + images.put(filePath, image); + return true; + } else { + return false; } - - } catch (Exception e) { - PupperLogger.error("ImageHelper/readGpuTextureData", "error:" + e); - return null; } + return true; } - @Nullable - @Deprecated - public GpuTexture convertToGpuTexture(Image image) { - if (image == null) { - return null; - } + public boolean load(File file) { - try { - RenderSystem.assertOnRenderThread(); + if (!images.containsKey(file.getName())) { - int width = image.getWidth(); - int height = image.getHeight(); - - // 获取图像数据 - ImageInfo info = image.getImageInfo(); - Data data = image.encodeToData(EncodedImageFormat.PNG); - if (data == null) { - return null; + try { + byte[] encoded = org.apache.commons.io.IOUtils.toByteArray(new FileInputStream(file)); + Image image; + try { + image = Image.makeDeferredFromEncodedBytes(ImageUtils.convertToPng(encoded)); + } catch (IOException e) { + return false; + } + images.put(file.getName(), image); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; } - - // 创建 GpuTexture - GpuTexture texture = RenderSystem.getDevice().createTexture( - () -> "SkiaImageTexture", - com.mojang.blaze3d.textures.TextureFormat.RGBA8, - width, - height, - 1 - ); - - return texture; - - } catch (Exception e) { - PupperLogger.error("ImageHelper/convertToGpuTexture", "error:" + e); - return null; - } - } - - /** - * 清理缓存 - */ - public void clearCache() { - // 关闭所有缓存的图像 - for (Image image : fileImages.values()) { - image.close(); - } - for (Image image : resourceImages.values()) { - image.close(); } - for (Image image : gpuTextureImages.values()) { - image.close(); - } - - fileImages.clear(); - resourceImages.clear(); - gpuTextureImages.clear(); - loadingFutures.clear(); - callbacks.clear(); - } - - /** - * 清理特定类型的缓存 - */ - public void clearFileCache() { - fileImages.values().forEach(Image::close); - fileImages.clear(); - } - - public void clearResourceCache() { - resourceImages.values().forEach(Image::close); - resourceImages.clear(); - } - - public void clearGpuTextureCache() { - gpuTextureImages.values().forEach(Image::close); - gpuTextureImages.clear(); - } - - /** - * 检查图像是否已缓存 - */ - public boolean isCached(String key) { - return fileImages.containsKey(key); - } - - public boolean isCached(Identifier identifier) { - return resourceImages.containsKey(identifier); - } - - public boolean isCached(GpuTexture texture) { - return gpuTextureImages.containsKey(texture); - } - /** - * 获取缓存统计信息 - */ - public CacheStats getCacheStats() { - return new CacheStats( - fileImages.size(), - resourceImages.size(), - gpuTextureImages.size(), - loadingFutures.size() - ); + return true; } - /** - * 预加载常用资源 - */ - public void preloadCommonResources() { + public Image get(String path) { - } - - @Unstable - public static ByteBuffer readGpuTextureDataCorrect(GpuTexture texture) { - if (texture == null) { - return null; + if (images.containsKey(path)) { + return images.get(path); } - RenderSystem.assertOnRenderThread(); - - try { - int width = texture.getWidth(0); - int height = texture.getHeight(0); - int pixelSize = texture.getFormat().pixelSize(); // 每个像素的字节数 - int bufferSize = width * height * pixelSize; - - try (GpuBuffer readBuffer = RenderSystem.getDevice().createBuffer( - () -> "GpuTextureReadBuffer", - BufferType.PIXEL_PACK, - BufferUsage.STREAM_READ, - bufferSize - )) { - CountDownLatch latch = new CountDownLatch(1); - ByteBuffer[] resultHolder = new ByteBuffer[1]; - // 创建命令编码器 - var commandEncoder = RenderSystem.getDevice().createCommandEncoder(); - - commandEncoder.copyTextureToBuffer( - texture, - readBuffer, - 0, - () -> { - try { - try (GpuBuffer.ReadView readView = commandEncoder.readBuffer(readBuffer)) { - - ByteBuffer pixelData = BufferUtils.createByteBuffer(bufferSize); - ByteBuffer sourceData = readView.data(); - if (sourceData != null) { - sourceData.rewind(); - pixelData.put(sourceData); - pixelData.flip(); - resultHolder[0] = pixelData; - } - } - } catch (Exception e) { - PupperLogger.error("ImageHelper/readGpuTextureDataCorrect", "copyTextureToBuffer error:" + e); - } finally { - latch.countDown(); - } - }, - 0 // mipLevel - ); - - // 等待操作完成(最大等待5秒) - boolean success = latch.await(5, TimeUnit.SECONDS); - if (!success) { - PupperLogger.error("ImageHelper/readGpuTextureDataCorrect", "读取纹理数据超时"); - return null; - } - - return resultHolder[0]; - - } - - } catch (Exception e) { - PupperLogger.error("ImageHelper/readGpuTextureDataCorrect", "error:" + e); - return null; - } - } - - public void close() { - clearCache(); - LOADER_EXECUTOR.shutdown(); + return null; } - public record CacheStats(int fileImages, int resourceImages, int gpuTextureImages, int loadingTasks) { + public Image get(int texture) { - @Override - public @NotNull String toString() { - return String.format( - "CacheStats{文件图像=%d, 资源图像=%d, GPU纹理=%d, 加载任务=%d}", - fileImages, resourceImages, gpuTextureImages, loadingTasks - ); - } - } - - private static final ImageHelper INSTANCE = new ImageHelper(); + if (textures.containsKey(texture)) { + return textures.get(texture); + } - public static ImageHelper getInstance() { - return INSTANCE; + return null; } } diff --git a/src/main/java/cn/pupperclient/utils/ImageUtils.java b/src/main/java/cn/pupperclient/utils/ImageUtils.java index cda9733..b5245d4 100644 --- a/src/main/java/cn/pupperclient/utils/ImageUtils.java +++ b/src/main/java/cn/pupperclient/utils/ImageUtils.java @@ -1,39 +1,65 @@ package cn.pupperclient.utils; -import java.awt.Color; +import javax.imageio.ImageIO; +import java.awt.*; import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; public class ImageUtils { - public static Color calculateAverageColor(BufferedImage image) { - long totalRed = 0; - long totalGreen = 0; - long totalBlue = 0; - int width = image.getWidth(); - int height = image.getHeight(); - int totalPixels = width * height; - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - Color pixelColor = new Color(image.getRGB(x, y)); - totalRed += pixelColor.getRed(); - totalGreen += pixelColor.getGreen(); - totalBlue += pixelColor.getBlue(); - } - } - - int averageRed = (int) (totalRed / totalPixels); - int averageGreen = (int) (totalGreen / totalPixels); - int averageBlue = (int) (totalBlue / totalPixels); - - return new Color(averageRed, averageGreen, averageBlue); - } - - public static int[] imageToPixels(BufferedImage image) { - - int width = image.getWidth(); - int height = image.getHeight(); - - return image.getRGB(0, 0, width, height, null, 0, width); - } + public static Color calculateAverageColor(BufferedImage image) { + long totalRed = 0; + long totalGreen = 0; + long totalBlue = 0; + int width = image.getWidth(); + int height = image.getHeight(); + int totalPixels = width * height; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + Color pixelColor = new Color(image.getRGB(x, y)); + totalRed += pixelColor.getRed(); + totalGreen += pixelColor.getGreen(); + totalBlue += pixelColor.getBlue(); + } + } + + int averageRed = (int) (totalRed / totalPixels); + int averageGreen = (int) (totalGreen / totalPixels); + int averageBlue = (int) (totalBlue / totalPixels); + + return new Color(averageRed, averageGreen, averageBlue); + } + + public static int[] imageToPixels(BufferedImage image) { + + int width = image.getWidth(); + int height = image.getHeight(); + + return image.getRGB(0, 0, width, height, null, 0, width); + } + + + public static byte[] convertToPng(byte[] bytes) throws IOException { + BufferedImage image = createImageFromBytes(bytes); + if (image == null) { + return bytes; + } + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageIO.write(image, "png", outputStream); + return outputStream.toByteArray(); + } + + public static BufferedImage createImageFromBytes(byte[] imageData) { + ByteArrayInputStream bais = new ByteArrayInputStream(imageData); + try { + return ImageIO.read(bais); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/resources/assets/pupper/shaders/blur.vert b/src/main/resources/assets/pupper/shaders/blur.vert index 24684cb..f588849 100644 --- a/src/main/resources/assets/pupper/shaders/blur.vert +++ b/src/main/resources/assets/pupper/shaders/blur.vert @@ -2,11 +2,10 @@ precision lowp float; -layout (location = 0) in vec3 Position; -layout (location = 1) in vec2 UV0; +layout (location = 0) in vec2 pos; out vec2 uv; void main() { - gl_Position = vec4(Position, 1.0); - uv = UV0; + gl_Position = vec4(pos, 0, 1); + uv = pos * .5 + .5; } diff --git a/src/main/resources/assets/pupper/shaders/blur_down.frag b/src/main/resources/assets/pupper/shaders/blur_down.frag index 75454f5..acb880c 100644 --- a/src/main/resources/assets/pupper/shaders/blur_down.frag +++ b/src/main/resources/assets/pupper/shaders/blur_down.frag @@ -11,11 +11,11 @@ uniform float uOffset; void main() { color = ( - texture(uTexture, uv) * 4.0 + + texture(uTexture, uv) * 4 + texture(uTexture, uv - uHalfTexelSize.xy * uOffset) + texture(uTexture, uv + uHalfTexelSize.xy * uOffset) + texture(uTexture, uv + vec2(uHalfTexelSize.x, -uHalfTexelSize.y) * uOffset) + texture(uTexture, uv - vec2(uHalfTexelSize.x, -uHalfTexelSize.y) * uOffset) - ) / 8.0; - color.a = 1.0; + ) / 8; + color.a = 1; } diff --git a/src/main/resources/assets/pupper/shaders/blur_up.frag b/src/main/resources/assets/pupper/shaders/blur_up.frag index dfd8931..28a006d 100644 --- a/src/main/resources/assets/pupper/shaders/blur_up.frag +++ b/src/main/resources/assets/pupper/shaders/blur_up.frag @@ -9,11 +9,6 @@ uniform sampler2D uTexture; uniform vec2 uHalfTexelSize; uniform float uOffset; -vec4 safeTexture2D(sampler2D tex, vec2 uv) { - uv = clamp(uv, 0.001, 0.999); // 防止在边缘采样 - return texture(tex, uv); -} - void main() { color = ( texture(uTexture, uv + vec2(- uHalfTexelSize.x * 2, 0) * uOffset) + @@ -27,4 +22,3 @@ void main() { ) / 12; color.a = 1; } - diff --git a/src/main/resources/assets/pupper/shaders/passthrough.vert b/src/main/resources/assets/pupper/shaders/passthrough.vert index 83cb6a9..f588849 100644 --- a/src/main/resources/assets/pupper/shaders/passthrough.vert +++ b/src/main/resources/assets/pupper/shaders/passthrough.vert @@ -2,8 +2,7 @@ precision lowp float; -layout (location = 0) in vec3 Position; -layout (location = 1) in vec2 UV0; +layout (location = 0) in vec2 pos; out vec2 uv; void main() { diff --git a/src/main/resources/pupper.accesswidener b/src/main/resources/pupper.accesswidener index 97879e6..4ea2f24 100644 --- a/src/main/resources/pupper.accesswidener +++ b/src/main/resources/pupper.accesswidener @@ -39,3 +39,5 @@ accessible field net/minecraft/client/render/entity/EntityRenderDispatcher model accessible method com/mojang/blaze3d/pipeline/RenderPipeline$Builder ()V accessible method com/mojang/blaze3d/pipeline/RenderPipeline$Builder withSnippet (Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet;)V + +accessible field net/minecraft/client/gl/Framebuffer index I