2025-11-24 22:52:51 +03:00

229 lines
8.6 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.collect.Sets
* it.unimi.dsi.fastutil.ints.Int2ObjectMap
* it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
* it.unimi.dsi.fastutil.ints.IntArrayList
* it.unimi.dsi.fastutil.ints.IntCollection
* it.unimi.dsi.fastutil.ints.IntList
* it.unimi.dsi.fastutil.ints.IntOpenHashSet
* org.jspecify.annotations.Nullable
*/
package net.minecraft.client.gui.font;
import com.google.common.collect.Sets;
import com.mojang.blaze3d.font.GlyphBitmap;
import com.mojang.blaze3d.font.GlyphInfo;
import com.mojang.blaze3d.font.GlyphProvider;
import com.mojang.blaze3d.font.UnbakedGlyph;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import net.minecraft.client.gui.GlyphSource;
import net.minecraft.client.gui.font.CodepointMap;
import net.minecraft.client.gui.font.FontOption;
import net.minecraft.client.gui.font.GlyphStitcher;
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
import net.minecraft.client.gui.font.glyphs.EffectGlyph;
import net.minecraft.client.gui.font.glyphs.SpecialGlyphs;
import net.minecraft.network.chat.Style;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import org.jspecify.annotations.Nullable;
public class FontSet
implements AutoCloseable {
private static final float LARGE_FORWARD_ADVANCE = 32.0f;
private static final BakedGlyph INVISIBLE_MISSING_GLYPH = new BakedGlyph(){
@Override
public GlyphInfo info() {
return SpecialGlyphs.MISSING;
}
@Override
public @Nullable TextRenderable.Styled createGlyph(float x, float y, int color, int shadowColor, Style style, float boldOffset, float shadowOffset) {
return null;
}
};
private final GlyphStitcher stitcher;
private final UnbakedGlyph.Stitcher wrappedStitcher = new UnbakedGlyph.Stitcher(){
@Override
public BakedGlyph stitch(GlyphInfo glyphInfo, GlyphBitmap glyphBitmap) {
return Objects.requireNonNullElse(FontSet.this.stitcher.stitch(glyphInfo, glyphBitmap), FontSet.this.missingGlyph);
}
@Override
public BakedGlyph getMissing() {
return FontSet.this.missingGlyph;
}
};
private List<GlyphProvider.Conditional> allProviders = List.of();
private List<GlyphProvider> activeProviders = List.of();
private final Int2ObjectMap<IntList> glyphsByWidth = new Int2ObjectOpenHashMap();
private final CodepointMap<SelectedGlyphs> glyphCache = new CodepointMap(SelectedGlyphs[]::new, x$0 -> new SelectedGlyphs[x$0][]);
private final IntFunction<SelectedGlyphs> glyphGetter = this::computeGlyphInfo;
private BakedGlyph missingGlyph = INVISIBLE_MISSING_GLYPH;
private final Supplier<BakedGlyph> missingGlyphGetter = () -> this.missingGlyph;
private final SelectedGlyphs missingSelectedGlyphs = new SelectedGlyphs(this.missingGlyphGetter, this.missingGlyphGetter);
private @Nullable EffectGlyph whiteGlyph;
private final GlyphSource anyGlyphs = new Source(false);
private final GlyphSource nonFishyGlyphs = new Source(true);
public FontSet(GlyphStitcher stitcher) {
this.stitcher = stitcher;
}
public void reload(List<GlyphProvider.Conditional> providers, Set<FontOption> options) {
this.allProviders = providers;
this.reload(options);
}
public void reload(Set<FontOption> options) {
this.activeProviders = List.of();
this.resetTextures();
this.activeProviders = this.selectProviders(this.allProviders, options);
}
private void resetTextures() {
this.stitcher.reset();
this.glyphCache.clear();
this.glyphsByWidth.clear();
this.missingGlyph = Objects.requireNonNull(SpecialGlyphs.MISSING.bake(this.stitcher));
this.whiteGlyph = SpecialGlyphs.WHITE.bake(this.stitcher);
}
private List<GlyphProvider> selectProviders(List<GlyphProvider.Conditional> providers, Set<FontOption> options) {
IntOpenHashSet supportedGlyphs = new IntOpenHashSet();
ArrayList<GlyphProvider> selectedProviders = new ArrayList<GlyphProvider>();
for (GlyphProvider.Conditional conditionalProvider : providers) {
if (!conditionalProvider.filter().apply(options)) continue;
selectedProviders.add(conditionalProvider.provider());
supportedGlyphs.addAll((IntCollection)conditionalProvider.provider().getSupportedGlyphs());
}
HashSet usedProviders = Sets.newHashSet();
supportedGlyphs.forEach(codepoint -> {
for (GlyphProvider provider : selectedProviders) {
UnbakedGlyph glyph = provider.getGlyph(codepoint);
if (glyph == null) continue;
usedProviders.add(provider);
if (glyph.info() == SpecialGlyphs.MISSING) break;
((IntList)this.glyphsByWidth.computeIfAbsent(Mth.ceil(glyph.info().getAdvance(false)), w -> new IntArrayList())).add(codepoint);
break;
}
});
return selectedProviders.stream().filter(usedProviders::contains).toList();
}
@Override
public void close() {
this.stitcher.close();
}
private static boolean hasFishyAdvance(GlyphInfo glyph) {
float advance = glyph.getAdvance(false);
if (advance < 0.0f || advance > 32.0f) {
return true;
}
float boldAdvance = glyph.getAdvance(true);
return boldAdvance < 0.0f || boldAdvance > 32.0f;
}
private SelectedGlyphs computeGlyphInfo(int codepoint) {
DelayedBake firstGlyph = null;
for (GlyphProvider provider : this.activeProviders) {
UnbakedGlyph glyph = provider.getGlyph(codepoint);
if (glyph == null) continue;
if (firstGlyph == null) {
firstGlyph = new DelayedBake(glyph);
}
if (FontSet.hasFishyAdvance(glyph.info())) continue;
if (firstGlyph.unbaked == glyph) {
return new SelectedGlyphs(firstGlyph, firstGlyph);
}
return new SelectedGlyphs(firstGlyph, new DelayedBake(glyph));
}
if (firstGlyph != null) {
return new SelectedGlyphs(firstGlyph, this.missingGlyphGetter);
}
return this.missingSelectedGlyphs;
}
private SelectedGlyphs getGlyph(int codepoint) {
return this.glyphCache.computeIfAbsent(codepoint, this.glyphGetter);
}
public BakedGlyph getRandomGlyph(RandomSource random, int width) {
IntList chars = (IntList)this.glyphsByWidth.get(width);
if (chars != null && !chars.isEmpty()) {
return this.getGlyph(chars.getInt(random.nextInt(chars.size()))).nonFishy().get();
}
return this.missingGlyph;
}
public EffectGlyph whiteGlyph() {
return Objects.requireNonNull(this.whiteGlyph);
}
public GlyphSource source(boolean nonFishyOnly) {
return nonFishyOnly ? this.nonFishyGlyphs : this.anyGlyphs;
}
private record SelectedGlyphs(Supplier<BakedGlyph> any, Supplier<BakedGlyph> nonFishy) {
private Supplier<BakedGlyph> select(boolean filterFishy) {
return filterFishy ? this.nonFishy : this.any;
}
}
public class Source
implements GlyphSource {
private final boolean filterFishyGlyphs;
public Source(boolean filterFishyGlyphs) {
this.filterFishyGlyphs = filterFishyGlyphs;
}
@Override
public BakedGlyph getGlyph(int codepoint) {
return FontSet.this.getGlyph(codepoint).select(this.filterFishyGlyphs).get();
}
@Override
public BakedGlyph getRandomGlyph(RandomSource random, int width) {
return FontSet.this.getRandomGlyph(random, width);
}
}
private class DelayedBake
implements Supplier<BakedGlyph> {
private final UnbakedGlyph unbaked;
private @Nullable BakedGlyph baked;
private DelayedBake(UnbakedGlyph unbaked) {
this.unbaked = unbaked;
}
@Override
public BakedGlyph get() {
if (this.baked == null) {
this.baked = this.unbaked.bake(FontSet.this.wrappedStitcher);
}
return this.baked;
}
}
}