diff --git a/src/client/java/net/vinrobot/mcemote/client/font/CustomFontStorage.java b/src/client/java/net/vinrobot/mcemote/client/font/CustomFontStorage.java new file mode 100644 index 0000000..61b90f0 --- /dev/null +++ b/src/client/java/net/vinrobot/mcemote/client/font/CustomFontStorage.java @@ -0,0 +1,56 @@ +package net.vinrobot.mcemote.client.font; + +import net.minecraft.client.font.BuiltinEmptyGlyph; +import net.minecraft.client.font.Font; +import net.minecraft.client.font.FontStorage; +import net.minecraft.client.font.Glyph; +import net.minecraft.client.font.GlyphRenderer; +import net.minecraft.client.texture.TextureManager; +import net.minecraft.util.Identifier; + +import java.util.List; + +public abstract class CustomFontStorage extends FontStorage { + private final Identifier identifier; + private final TextureManager textureManager; + private final GlyphRenderer blankGlyphRenderer; + + public CustomFontStorage(final TextureManager textureManager, final Identifier id) { + super(textureManager, id); + this.identifier = id; + this.textureManager = textureManager; + + // blankGlyphRenderer is private in FontStorage, but we can get it from the getObfuscatedGlyphRenderer + // because if it doesn't find the glyph, it returns the blankGlyphRenderer. + this.blankGlyphRenderer = super.getObfuscatedGlyphRenderer(BuiltinEmptyGlyph.MISSING); + } + + public final Identifier getIdentifier() { + return this.identifier; + } + + protected TextureManager getTextureManager() { + return this.textureManager; + } + + @Override + public void setFonts(final List fonts) { + throw new UnsupportedOperationException(); + } + + @Override + public abstract Glyph getGlyph(int codePoint, boolean validateAdvance); + + @Override + public abstract GlyphRenderer getGlyphRenderer(int codePoint); + + @Override + public GlyphRenderer getObfuscatedGlyphRenderer(final Glyph glyph) { + // Don't obfuscate the glyph by default, children can override this. + return this.blankGlyphRenderer; + } + + public GlyphRenderer getBlankGlyphRenderer() { + return this.blankGlyphRenderer; + } +} diff --git a/src/client/java/net/vinrobot/mcemote/client/font/EmoteFontStorage.java b/src/client/java/net/vinrobot/mcemote/client/font/EmoteFontStorage.java index 83b3eb5..8446b8e 100644 --- a/src/client/java/net/vinrobot/mcemote/client/font/EmoteFontStorage.java +++ b/src/client/java/net/vinrobot/mcemote/client/font/EmoteFontStorage.java @@ -1,18 +1,15 @@ package net.vinrobot.mcemote.client.font; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; import net.minecraft.client.font.BuiltinEmptyGlyph; -import net.minecraft.client.font.FontStorage; import net.minecraft.client.font.Glyph; import net.minecraft.client.font.GlyphRenderer; import net.minecraft.client.texture.TextureManager; import net.minecraft.util.Identifier; -import net.vinrobot.mcemote.MinecraftEmoteMod; import net.vinrobot.mcemote.client.helpers.FutureHelper; import net.vinrobot.mcemote.client.imageio.NativeFrame; import net.vinrobot.mcemote.client.text.EmotesManager; +import java.io.IOException; import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -22,8 +19,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -@Environment(EnvType.CLIENT) -public class EmoteFontStorage extends FontStorage { +public class EmoteFontStorage extends CustomFontStorage { public static final Identifier IDENTIFIER = new Identifier("mcemote.fonts", "emotes"); public static final float GLYPH_HEIGHT = 8; @@ -38,47 +34,52 @@ public EmoteFontStorage(TextureManager textureManager, EmotesManager emotesManag } @Override - public Glyph getGlyph(int codePoint, boolean validateAdvance) { + public void close() { + super.close(); + this.clearCache(); + } + + public void clearCache() { + this.framesCache.clear(); + this.glyphRendererCache.clear(); + } + + @Override + public Glyph getGlyph(final int codePoint, final boolean validateAdvance) { try { return FutureHelper.asOptional(this.framesCache.computeIfAbsent(codePoint, this::asyncLoadAnimatedGlyph)) - .map(m -> m.getGlyphAt(Instant.now())) - .orElse(BuiltinEmptyGlyph.MISSING); + .map(m -> m.getGlyphAt(Instant.now())).orElse(BuiltinEmptyGlyph.MISSING); } catch (RuntimeException ex) { return BuiltinEmptyGlyph.MISSING; } } - private Future asyncLoadAnimatedGlyph(int codePoint) { + private Future asyncLoadAnimatedGlyph(final int codePoint) { return this.executorService.submit(() -> this.loadAnimatedGlyph(codePoint)); } - private AnimatedGlyph loadAnimatedGlyph(int codePoint) { - try { - final Emote emote = this.emotesManager.getByCodePoint(codePoint).orElseThrow(); - - final int width = emote.getWidth(); - final int height = emote.getHeight(); - final float advance = width * GLYPH_HEIGHT / height; - final float oversample = height / GLYPH_HEIGHT; - final NativeFrame[] frames = emote.loadFrames(); + private AnimatedGlyph loadAnimatedGlyph(final int codePoint) throws IOException { + final Emote emote = this.emotesManager.getByCodePoint(codePoint).orElseThrow(); - final AnimatedGlyph.Frame[] animatedFrames = new AnimatedGlyph.Frame[frames.length]; - for (int i = 0; i < frames.length; i++) { - final NativeFrame frame = frames[i]; - final Glyph glyph = new NativeImageGlyph(frame.image(), advance, oversample); - animatedFrames[i] = new AnimatedGlyph.Frame(glyph, frame.duration()); - } + final int width = emote.getWidth(); + final int height = emote.getHeight(); + final float advance = width * GLYPH_HEIGHT / height; + final float oversample = height / GLYPH_HEIGHT; + final NativeFrame[] frames = emote.loadFrames(); - return new AnimatedGlyph(animatedFrames); - } catch (Exception ex) { - MinecraftEmoteMod.LOGGER.error("Unable to load emote", ex); - throw new RuntimeException(ex); + final AnimatedGlyph.Frame[] animatedFrames = new AnimatedGlyph.Frame[frames.length]; + for (int i = 0; i < frames.length; ++i) { + final NativeFrame frame = frames[i]; + final Glyph glyph = new NativeImageGlyph(frame.image(), advance, oversample); + animatedFrames[i] = new AnimatedGlyph.Frame(glyph, frame.duration()); } + + return new AnimatedGlyph(animatedFrames); } @Override - public GlyphRenderer getGlyphRenderer(int codePoint) { - final Glyph glyph = this.getGlyph(codePoint, false); - return this.glyphRendererCache.computeIfAbsent(glyph, (g) -> g.bake(this::getGlyphRenderer)); + public GlyphRenderer getGlyphRenderer(final int codePoint) { + final Glyph glyph = this.getGlyph(codePoint, true); + return this.glyphRendererCache.computeIfAbsent(glyph, g -> g.bake(this::getGlyphRenderer)); } } diff --git a/src/main/resources/mcemote.accesswidener b/src/main/resources/mcemote.accesswidener index 9a84b44..a76573e 100644 --- a/src/main/resources/mcemote.accesswidener +++ b/src/main/resources/mcemote.accesswidener @@ -2,7 +2,4 @@ accessWidener v1 named accessible class net/minecraft/client/font/FontManager$ProviderIndex -accessible class net/minecraft/client/font/FontStorage$GlyphPair -accessible method net/minecraft/client/font/FontStorage$GlyphPair (Lnet/minecraft/client/font/Glyph;Lnet/minecraft/client/font/Glyph;)V - accessible method net/minecraft/client/font/FontStorage getGlyphRenderer (Lnet/minecraft/client/font/RenderableGlyph;)Lnet/minecraft/client/font/GlyphRenderer;