aboutsummaryrefslogtreecommitdiff
path: root/common/src/main/java/dev/isxander/yacl
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/main/java/dev/isxander/yacl')
-rw-r--r--common/src/main/java/dev/isxander/yacl/api/OptionDescription.java8
-rw-r--r--common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java28
-rw-r--r--common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java20
-rw-r--r--common/src/main/java/dev/isxander/yacl/mixin/MinecraftMixin.java16
4 files changed, 55 insertions, 17 deletions
diff --git a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java
index 3b28a65..22eebc9 100644
--- a/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java
+++ b/common/src/main/java/dev/isxander/yacl/api/OptionDescription.java
@@ -28,12 +28,14 @@ public interface OptionDescription {
Builder image(ResourceLocation image, int width, int height);
Builder image(Path path, ResourceLocation uniqueLocation);
+ Builder webpImage(ResourceLocation image);
+ Builder webpImage(Path path, ResourceLocation uniqueLocation);
+
+ @Deprecated
Builder gifImage(ResourceLocation image);
+ @Deprecated
Builder gifImage(Path path, ResourceLocation uniqueLocation);
- Builder webpImage(ResourceLocation image, int frameDelayMS);
- Builder webpImage(Path path, ResourceLocation uniqueLocation, int frameDelayMS);
-
OptionDescription build();
}
}
diff --git a/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java b/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java
index 8ea8ba3..1376a27 100644
--- a/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java
+++ b/common/src/main/java/dev/isxander/yacl/gui/ImageRenderer.java
@@ -149,11 +149,11 @@ public interface ImageRenderer extends AutoCloseable {
return createGIF(resource.open(), textureLocation);
}
- public static AnimatedNativeImageBacked createWEBPFromTexture(ResourceLocation textureLocation, int frameDelayMS) throws IOException {
+ public static AnimatedNativeImageBacked createWEBPFromTexture(ResourceLocation textureLocation) throws IOException {
ResourceManager resourceManager = Minecraft.getInstance().getResourceManager();
Resource resource = resourceManager.getResource(textureLocation).orElseThrow();
- return createWEBP(resource.open(), textureLocation, frameDelayMS);
+ return createWEBP(resource.open(), textureLocation);
}
public static AnimatedNativeImageBacked createGIF(InputStream is, ResourceLocation uniqueLocation) {
@@ -173,10 +173,30 @@ public interface ImageRenderer extends AutoCloseable {
}
}
- public static AnimatedNativeImageBacked createWEBP(InputStream is, ResourceLocation uniqueLocation, int frameDelayMS) {
+ public static AnimatedNativeImageBacked createWEBP(InputStream is, ResourceLocation uniqueLocation) {
try (is) {
- ImageReader reader = ImageIO.getImageReadersBySuffix("webp").next();
+ ImageReader reader = new WebPImageReaderSpi().createReaderInstance();
reader.setInput(ImageIO.createImageInputStream(is));
+
+ // WebP reader does not expose frame delay, prepare for reflection hell
+ int frameDelayMS;
+ reader.getNumImages(true); // Force reading of all frames
+ try {
+ Class<?> webpReaderClass = Class.forName("com.twelvemonkeys.imageio.plugins.webp.WebPImageReader");
+ Field framesField = webpReaderClass.getDeclaredField("frames");
+ framesField.setAccessible(true);
+ List<?> frames = (List<?>) framesField.get(reader);
+ Object firstFrame = frames.get(0);
+
+ Class<?> animationFrameClass = Class.forName("com.twelvemonkeys.imageio.plugins.webp.AnimationFrame");
+ Field durationField = animationFrameClass.getDeclaredField("duration");
+ durationField.setAccessible(true);
+ frameDelayMS = (int) durationField.get(firstFrame);
+ } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ // that was fun
+
return createFromImageReader(reader, frameDelayMS, uniqueLocation);
} catch (IOException e) {
throw new RuntimeException(e);
diff --git a/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java b/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java
index f57a410..c866b43 100644
--- a/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java
+++ b/common/src/main/java/dev/isxander/yacl/impl/OptionDescriptionImpl.java
@@ -38,7 +38,7 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip
Validate.isTrue(width > 0, "Width must be greater than 0!");
Validate.isTrue(height > 0, "Height must be greater than 0!");
- this.image = CompletableFuture.completedFuture(Optional.of(new ImageRenderer.TextureBacked(image, width, height)));
+ this.image = ImageRenderer.getOrMakeSync(image, () -> Optional.of(new ImageRenderer.TextureBacked(image, width, height)));
imageUnset = false;
return this;
}
@@ -46,7 +46,7 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip
@Override
public Builder image(Path path, ResourceLocation uniqueLocation) {
Validate.isTrue(imageUnset, "Image already set!");
- this.image = CompletableFuture.supplyAsync(() -> ImageRenderer.NativeImageBacked.createFromPath(path, uniqueLocation));
+ this.image = ImageRenderer.getOrMakeAsync(uniqueLocation, () -> ImageRenderer.NativeImageBacked.createFromPath(path, uniqueLocation));
imageUnset = false;
return this;
}
@@ -54,7 +54,7 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip
@Override
public Builder gifImage(ResourceLocation image) {
Validate.isTrue(imageUnset, "Image already set!");
- this.image = CompletableFuture.supplyAsync(() -> {
+ this.image = ImageRenderer.getOrMakeAsync(image, () -> {
try {
return Optional.of(ImageRenderer.AnimatedNativeImageBacked.createGIFFromTexture(image));
} catch (IOException e) {
@@ -69,7 +69,7 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip
@Override
public Builder gifImage(Path path, ResourceLocation uniqueLocation) {
Validate.isTrue(imageUnset, "Image already set!");
- this.image = CompletableFuture.supplyAsync(() -> {
+ this.image = ImageRenderer.getOrMakeAsync(uniqueLocation, () -> {
try {
return Optional.of(ImageRenderer.AnimatedNativeImageBacked.createGIF(new FileInputStream(path.toFile()), uniqueLocation));
} catch (IOException e) {
@@ -82,11 +82,11 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip
}
@Override
- public Builder webpImage(ResourceLocation image, int frameDelayMS) {
+ public Builder webpImage(ResourceLocation image) {
Validate.isTrue(imageUnset, "Image already set!");
- this.image = CompletableFuture.supplyAsync(() -> {
+ this.image = ImageRenderer.getOrMakeAsync(image, () -> {
try {
- return Optional.of(ImageRenderer.AnimatedNativeImageBacked.createWEBPFromTexture(image, frameDelayMS));
+ return Optional.of(ImageRenderer.AnimatedNativeImageBacked.createWEBPFromTexture(image));
} catch (IOException e) {
e.printStackTrace();
return Optional.empty();
@@ -97,11 +97,11 @@ public record OptionDescriptionImpl(Component descriptiveName, Component descrip
}
@Override
- public Builder webpImage(Path path, ResourceLocation uniqueLocation, int frameDelayMS) {
+ public Builder webpImage(Path path, ResourceLocation uniqueLocation) {
Validate.isTrue(imageUnset, "Image already set!");
- this.image = CompletableFuture.supplyAsync(() -> {
+ this.image = ImageRenderer.getOrMakeAsync(uniqueLocation, () -> {
try {
- return Optional.of(ImageRenderer.AnimatedNativeImageBacked.createWEBP(new FileInputStream(path.toFile()), uniqueLocation, frameDelayMS));
+ return Optional.of(ImageRenderer.AnimatedNativeImageBacked.createWEBP(new FileInputStream(path.toFile()), uniqueLocation));
} catch (IOException e) {
e.printStackTrace();
return Optional.empty();
diff --git a/common/src/main/java/dev/isxander/yacl/mixin/MinecraftMixin.java b/common/src/main/java/dev/isxander/yacl/mixin/MinecraftMixin.java
new file mode 100644
index 0000000..c33eed7
--- /dev/null
+++ b/common/src/main/java/dev/isxander/yacl/mixin/MinecraftMixin.java
@@ -0,0 +1,16 @@
+package dev.isxander.yacl.mixin;
+
+import dev.isxander.yacl.gui.ImageRenderer;
+import net.minecraft.client.Minecraft;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(Minecraft.class)
+public class MinecraftMixin {
+ @Inject(method = "close", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/telemetry/ClientTelemetryManager;close()V"))
+ private void closeImages(CallbackInfo ci) {
+ ImageRenderer.closeAll();
+ }
+}