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

212 lines
8.7 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.mojang.datafixers.kinds.App
* com.mojang.datafixers.kinds.Applicative
* com.mojang.datafixers.util.Either
* com.mojang.logging.LogUtils
* com.mojang.serialization.Codec
* com.mojang.serialization.DataResult
* com.mojang.serialization.MapCodec
* com.mojang.serialization.codecs.RecordCodecBuilder
* it.unimi.dsi.fastutil.ints.IntSet
* it.unimi.dsi.fastutil.ints.IntSets
* org.jspecify.annotations.Nullable
* org.slf4j.Logger
*/
package net.minecraft.client.gui.font.providers;
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 com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
import com.mojang.datafixers.kinds.App;
import com.mojang.datafixers.kinds.Applicative;
import com.mojang.datafixers.util.Either;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSets;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import net.minecraft.client.gui.font.CodepointMap;
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
import net.minecraft.client.gui.font.providers.GlyphProviderDefinition;
import net.minecraft.client.gui.font.providers.GlyphProviderType;
import net.minecraft.resources.Identifier;
import net.minecraft.server.packs.resources.ResourceManager;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
public class BitmapProvider
implements GlyphProvider {
private static final Logger LOGGER = LogUtils.getLogger();
private final NativeImage image;
private final CodepointMap<Glyph> glyphs;
private BitmapProvider(NativeImage image, CodepointMap<Glyph> glyphs) {
this.image = image;
this.glyphs = glyphs;
}
@Override
public void close() {
this.image.close();
}
@Override
public @Nullable UnbakedGlyph getGlyph(int codepoint) {
return this.glyphs.get(codepoint);
}
@Override
public IntSet getSupportedGlyphs() {
return IntSets.unmodifiable((IntSet)this.glyphs.keySet());
}
private record Glyph(float scale, NativeImage image, int offsetX, int offsetY, int width, int height, int advance, int ascent) implements UnbakedGlyph
{
@Override
public GlyphInfo info() {
return GlyphInfo.simple(this.advance);
}
@Override
public BakedGlyph bake(UnbakedGlyph.Stitcher stitcher) {
return stitcher.stitch(this.info(), new GlyphBitmap(){
@Override
public float getOversample() {
return 1.0f / scale;
}
@Override
public int getPixelWidth() {
return width;
}
@Override
public int getPixelHeight() {
return height;
}
@Override
public float getBearingTop() {
return ascent;
}
@Override
public void upload(int x, int y, GpuTexture texture) {
RenderSystem.getDevice().createCommandEncoder().writeToTexture(texture, image, 0, 0, x, y, width, height, offsetX, offsetY);
}
@Override
public boolean isColored() {
return image.format().components() > 1;
}
});
}
}
public record Definition(Identifier file, int height, int ascent, int[][] codepointGrid) implements GlyphProviderDefinition
{
private static final Codec<int[][]> CODEPOINT_GRID_CODEC = Codec.STRING.listOf().xmap(input -> {
int lineCount = input.size();
int[][] result = new int[lineCount][];
for (int i = 0; i < lineCount; ++i) {
result[i] = ((String)input.get(i)).codePoints().toArray();
}
return result;
}, grid -> {
ArrayList<String> result = new ArrayList<String>(((int[][])grid).length);
for (int[] line : grid) {
result.add(new String(line, 0, line.length));
}
return result;
}).validate(Definition::validateDimensions);
public static final MapCodec<Definition> CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)Identifier.CODEC.fieldOf("file").forGetter(Definition::file), (App)Codec.INT.optionalFieldOf("height", (Object)8).forGetter(Definition::height), (App)Codec.INT.fieldOf("ascent").forGetter(Definition::ascent), (App)CODEPOINT_GRID_CODEC.fieldOf("chars").forGetter(Definition::codepointGrid)).apply((Applicative)i, Definition::new)).validate(Definition::validate);
private static DataResult<int[][]> validateDimensions(int[][] grid) {
int lineCount = grid.length;
if (lineCount == 0) {
return DataResult.error(() -> "Expected to find data in codepoint grid");
}
int[] firstLine = grid[0];
int lineWidth = firstLine.length;
if (lineWidth == 0) {
return DataResult.error(() -> "Expected to find data in codepoint grid");
}
for (int i = 1; i < lineCount; ++i) {
int[] line = grid[i];
if (line.length == lineWidth) continue;
return DataResult.error(() -> "Lines in codepoint grid have to be the same length (found: " + line.length + " codepoints, expected: " + lineWidth + "), pad with \\u0000");
}
return DataResult.success((Object)grid);
}
private static DataResult<Definition> validate(Definition builder) {
if (builder.ascent > builder.height) {
return DataResult.error(() -> "Ascent " + builder.ascent + " higher than height " + builder.height);
}
return DataResult.success((Object)builder);
}
@Override
public GlyphProviderType type() {
return GlyphProviderType.BITMAP;
}
@Override
public Either<GlyphProviderDefinition.Loader, GlyphProviderDefinition.Reference> unpack() {
return Either.left(this::load);
}
private GlyphProvider load(ResourceManager resourceManager) throws IOException {
Identifier texture = this.file.withPrefix("textures/");
try (InputStream resource = resourceManager.open(texture);){
NativeImage image = NativeImage.read(NativeImage.Format.RGBA, resource);
int w = image.getWidth();
int h = image.getHeight();
int glyphWidth = w / this.codepointGrid[0].length;
int glyphHeight = h / this.codepointGrid.length;
float pixelScale = (float)this.height / (float)glyphHeight;
CodepointMap<Glyph> charMap = new CodepointMap<Glyph>(Glyph[]::new, x$0 -> new Glyph[x$0][]);
for (int slotY = 0; slotY < this.codepointGrid.length; ++slotY) {
int linePos = 0;
for (int c : this.codepointGrid[slotY]) {
int actualGlyphWidth;
Glyph prev;
int slotX = linePos++;
if (c == 0 || (prev = charMap.put(c, new Glyph(pixelScale, image, slotX * glyphWidth, slotY * glyphHeight, glyphWidth, glyphHeight, (int)(0.5 + (double)((float)(actualGlyphWidth = this.getActualGlyphWidth(image, glyphWidth, glyphHeight, slotX, slotY)) * pixelScale)) + 1, this.ascent))) == null) continue;
LOGGER.warn("Codepoint '{}' declared multiple times in {}", (Object)Integer.toHexString(c), (Object)texture);
}
}
BitmapProvider bitmapProvider = new BitmapProvider(image, charMap);
return bitmapProvider;
}
}
private int getActualGlyphWidth(NativeImage image, int glyphWidth, int glyphHeight, int xGlyph, int yGlyph) {
int width;
for (width = glyphWidth - 1; width >= 0; --width) {
int xPixel = xGlyph * glyphWidth + width;
for (int y = 0; y < glyphHeight; ++y) {
int yPixel = yGlyph * glyphHeight + y;
if (image.getLuminanceOrAlpha(xPixel, yPixel) == 0) continue;
return width + 1;
}
}
return width + 1;
}
}
}