Skip to content

Commit

Permalink
Refactor FontStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinrobot committed Nov 19, 2023
1 parent 3edd4f8 commit e2e04b3
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -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<Font> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand All @@ -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<AnimatedGlyph> asyncLoadAnimatedGlyph(int codePoint) {
private Future<AnimatedGlyph> 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));
}
}
3 changes: 0 additions & 3 deletions src/main/resources/mcemote.accesswidener
Original file line number Diff line number Diff line change
Expand Up @@ -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 <init> (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;

0 comments on commit e2e04b3

Please sign in to comment.