Newest state of the font renderer
This commit is contained in:
parent
2dae61adbf
commit
7a7d9c1fd0
|
@ -1,25 +1,27 @@
|
|||
package speiger.src.coreengine;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||
import speiger.src.coreengine.assets.AssetLocation;
|
||||
import speiger.src.coreengine.assets.AssetManager;
|
||||
import speiger.src.coreengine.assets.base.IAsset;
|
||||
import speiger.src.coreengine.assets.base.IAssetPackage;
|
||||
import speiger.src.coreengine.assets.base.IAssetProvider;
|
||||
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
|
||||
import speiger.src.coreengine.rendering.gui.font.Font;
|
||||
import speiger.src.coreengine.rendering.gui.font.FontManager;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.GlythBaker;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData.GlythBaker;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.MissingGlyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.providers.IFontProvider;
|
||||
import speiger.src.coreengine.rendering.gui.font.providers.STBTrueTypeProvider;
|
||||
import speiger.src.coreengine.rendering.guiOld.renderer.provider.BitmapFontProvider;
|
||||
import speiger.src.coreengine.rendering.input.devices.FileDrop;
|
||||
import speiger.src.coreengine.rendering.input.devices.Joystick;
|
||||
import speiger.src.coreengine.rendering.input.devices.Keyboard;
|
||||
|
@ -32,6 +34,8 @@ import speiger.src.coreengine.rendering.models.buffers.VertexArray;
|
|||
import speiger.src.coreengine.rendering.shader.Shader;
|
||||
import speiger.src.coreengine.rendering.shader.SimpleShader;
|
||||
import speiger.src.coreengine.rendering.shader.uniform.base.TextureUniform;
|
||||
import speiger.src.coreengine.rendering.shader.uniform.vec.Matrix4fUniform;
|
||||
import speiger.src.coreengine.rendering.tesselation.buffer.IVertexBuilder;
|
||||
import speiger.src.coreengine.rendering.tesselation.buffer.VertexBuilder;
|
||||
import speiger.src.coreengine.rendering.tesselation.format.VertexTypes;
|
||||
import speiger.src.coreengine.rendering.textures.custom.Drawable;
|
||||
|
@ -48,7 +52,9 @@ public class NewInputTest {
|
|||
EventBus bus = new EventBus();
|
||||
WindowManager manager = new WindowManager();
|
||||
AssetManager assets = new AssetManager(List.of(IAssetPackage.of(IOUtils.getBaseLocation())));
|
||||
FontManager fonts = new FontManager();
|
||||
private Shader<TestShader> shaderTest = Shader.create(TestShader::new);
|
||||
private Shader<GuiShader> guiShader = Shader.create(GuiShader::new);
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -65,15 +71,17 @@ public class NewInputTest {
|
|||
manager.addDevices(Mouse.INSTANCE, Keyboard.INSTANCE, Joystick.INSTANCE, FileDrop.INSTANCE);
|
||||
Window window = manager.builder().title("Testing Engine").build();
|
||||
shaderTest.register();
|
||||
guiShader.register();
|
||||
assets.addListener(GLStateTracker.instance().shaders);
|
||||
assets.addListener(GLStateTracker.TEXTURE_TRACKER);
|
||||
assets.addListener(fonts);
|
||||
assets.reload();
|
||||
|
||||
System.out.println("Testing: "+GL.getCapabilities().OpenGL46);
|
||||
System.out.println("Testing: "+Integer.divideUnsigned(-1, 255));
|
||||
|
||||
IFontProvider provider = STBTrueTypeProvider.create(AssetLocation.of("font/roboto/font.json"), assets);
|
||||
BitmapFontProvider bit = loadProvider(AssetLocation.of("font/roboto.json"), 18.5F);
|
||||
guiShader.get().proView.set(new Matrix4f().ortho(0, 0, window.width(), window.height(), 1000, -1000));
|
||||
|
||||
int size = 512;
|
||||
int half = size >> 1;
|
||||
|
@ -87,12 +95,10 @@ public class NewInputTest {
|
|||
texture.fill(0, half, half, half, 0, 0, 255, 255);
|
||||
texture.process(true);
|
||||
window.visible(true);
|
||||
var instance = bit.getCharacter("C".codePointAt(0), false);
|
||||
System.out.println("Testing: "+instance.getWidth()+","+instance.getHeight());
|
||||
bus.register(KeyEvent.Key.class, T -> {
|
||||
if(T.key() == GLFW.GLFW_KEY_T) {
|
||||
T.cancel();
|
||||
texture.bind();
|
||||
// texture.bind();
|
||||
Drawable drawable = new Drawable(GLTextureFormat.RGBA, half, half);
|
||||
drawable.fill(0, 0, half, half, 255, 255, 0, 255);
|
||||
drawable.upload(base * 3, base * 3, base, base, base * 2, base * 2);
|
||||
|
@ -101,8 +107,8 @@ public class NewInputTest {
|
|||
else if(T.key() == GLFW.GLFW_KEY_Z) {
|
||||
T.cancel();
|
||||
System.out.println("Testing");
|
||||
texture.bind();
|
||||
UnbakedGlyth data = provider.glythData("C".codePointAt(0), 0);
|
||||
// texture.bind();
|
||||
GlythData data = provider.glythData("C".codePointAt(0), 0, 18.5F, 2F);
|
||||
data.bake(new GlythBaker() {
|
||||
@Override
|
||||
public Glyth bake(IGlythSheetInfo info) {
|
||||
|
@ -112,7 +118,20 @@ public class NewInputTest {
|
|||
System.out.println("Test2: "+width+", "+height);
|
||||
return null;
|
||||
}
|
||||
}, 1F, 1F);
|
||||
});
|
||||
}
|
||||
else if(T.key() == GLFW.GLFW_KEY_U) {
|
||||
MissingGlyth glyth = new MissingGlyth(18.5F, 4F);
|
||||
glyth.bake(new GlythBaker() {
|
||||
@Override
|
||||
public Glyth bake(IGlythSheetInfo info) {
|
||||
int width = info.width();
|
||||
int height = info.height();
|
||||
info.upload(half - (width >> 1), half - (height >> 1));
|
||||
System.out.println("Test3: "+width+", "+height);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -125,7 +144,15 @@ public class NewInputTest {
|
|||
builder.pos(-0.5F, 0.5F, 0).tex(0F, 0F).rgba(-1).endVertex();
|
||||
builder.pos(-0.5F, -0.5F, 0).tex(0F, 1F).rgba(-1).endVertex();
|
||||
|
||||
Font font = fonts.createFont(18.5F);
|
||||
TestModel model = new TestModel(builder.getBytes());
|
||||
TestModel[] guiModel = new TestModel[1];
|
||||
List<GLDraw> draws = new ObjectArrayList<>();
|
||||
font.drawText("Testing my Text", 50, 50, -1, new TexturedBuffer((K, V) -> {
|
||||
draws.addAll(K);
|
||||
guiModel[0] = new TestModel(V);
|
||||
System.out.println("Testing: "+V.length+" bytes, "+K.size());
|
||||
}));
|
||||
|
||||
GL11.glClearColor(0.2F, 0.55F, 0.66F, 1F);
|
||||
while(!window.shouldClose()) {
|
||||
|
@ -136,8 +163,16 @@ public class NewInputTest {
|
|||
shaderTest.get().texture.set(texture);
|
||||
model.bindArray();
|
||||
GLStateTracker.drawArrays(GLMode.TRIANGLES.glValue(), 6);
|
||||
|
||||
model.unbindArray();
|
||||
guiModel[0].bindArray();
|
||||
guiShader.bind();
|
||||
for(GLDraw draw : draws) {
|
||||
// System.out.println("Draw: "+draw.texture()+", "+draw.startVertex()+", "+draw.vertexCount());
|
||||
guiShader.get().texture.set(draw.texture());
|
||||
GLStateTracker.drawArrays(GLMode.TRIANGLES.glValue(), draw.startVertex(), draw.vertexCount());
|
||||
}
|
||||
guiModel[0].unbindArray();
|
||||
|
||||
window.handleInput();
|
||||
window.finishFrame();
|
||||
try { Thread.sleep(100); }
|
||||
|
@ -147,15 +182,56 @@ public class NewInputTest {
|
|||
manager.destroy();
|
||||
}
|
||||
|
||||
private BitmapFontProvider loadProvider(AssetLocation location, float desiredSize)
|
||||
{
|
||||
try(IAsset asset = assets.getAsset(location))
|
||||
{
|
||||
JsonObject obj = asset.json();
|
||||
return BitmapFontProvider.load(obj, desiredSize, null);
|
||||
public static class TexturedBuffer implements Font.TexturedBuffer {
|
||||
int previousId = -1;
|
||||
int lastVertex = 0;
|
||||
List<GLDraw> draws = new ObjectArrayList<>();
|
||||
VertexBuilder builder;
|
||||
BiConsumer<List<GLDraw>, byte[]> callbacks;
|
||||
|
||||
public TexturedBuffer(BiConsumer<List<GLDraw>, byte[]> callbacks) {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
catch(Exception e) { e.printStackTrace(); }
|
||||
return null;
|
||||
|
||||
@Override
|
||||
public IVertexBuilder builderForTexture(int textureId) {
|
||||
if(builder == null) {
|
||||
builder = new VertexBuilder(100000);
|
||||
builder.start(GLMode.TRIANGLES, VertexTypes.TESTING);
|
||||
previousId = textureId;
|
||||
System.out.println("Texture: "+textureId);
|
||||
}
|
||||
else if(previousId != textureId) {
|
||||
int count = builder.size() - lastVertex;
|
||||
draws.add(new GLDraw(previousId, lastVertex, count));
|
||||
lastVertex+=count;
|
||||
previousId = textureId;
|
||||
System.out.println("Texture: "+textureId);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
int count = builder.size() - lastVertex;
|
||||
draws.add(new GLDraw(previousId, lastVertex, count));
|
||||
callbacks.accept(draws, builder.getBytes());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static record GLDraw(int texture, int startVertex, int vertexCount) {
|
||||
|
||||
}
|
||||
|
||||
public static class GuiShader extends SimpleShader {
|
||||
public TextureUniform texture = uniforms.addTexture("texture", 0);
|
||||
public Matrix4fUniform proView = uniforms.addMat("proViewMatrix", new Matrix4f());
|
||||
|
||||
public GuiShader(IAssetProvider provider) {
|
||||
super(provider, "gui_shader", AssetLocation.of("shader/testGui/vertex.vs"), AssetLocation.of("shader/testGui/fragment.fs"), "in_position", "in_tex", "in_color");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestShader extends SimpleShader {
|
||||
|
|
|
@ -32,4 +32,8 @@ public record AssetFilter(String prefix, String extension) {
|
|||
return provider.listAllAssets(prefix, T -> T.endsWith(extension));
|
||||
}
|
||||
|
||||
public Map<AssetLocation, MultiAsset> multiRoot(IAssetProvider provider) {
|
||||
return provider.listAllAssets(prefix, T -> T.endsWith(extension) && !T.isInFolder(prefix));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ public final class AssetLocation implements Comparable<AssetLocation> {
|
|||
public boolean isInFolder() { return location.contains("/"); }
|
||||
public boolean isInFolder(String prefix) { return location.indexOf("/", prefix.length()+1) >= 0; }
|
||||
|
||||
public AssetLocation prefix(String prefix) {
|
||||
return of(domain, prefix+"/"+location);
|
||||
}
|
||||
|
||||
public AssetLocation subAsset(String subPath) {
|
||||
return of(domain+":"+location+"/"+subPath);
|
||||
}
|
||||
|
@ -67,8 +71,40 @@ public final class AssetLocation implements Comparable<AssetLocation> {
|
|||
return LOCATION.computeIfAbsent(location.indexOf(":") == -1 ? "base:"+location : location, AssetLocation::compute);
|
||||
}
|
||||
|
||||
public static final AssetLocation tryOf(String location) {
|
||||
try { return of(location); }
|
||||
catch(Exception e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final AssetLocation compute(String s) {
|
||||
int index = s.indexOf(":");
|
||||
return new AssetLocation(s.substring(0, index), s.substring(index + 1));
|
||||
String domain = s.substring(0, index);
|
||||
String path = s.substring(index + 1);
|
||||
if(!isValidDomain(domain)) throw new IllegalArgumentException("Non [a-zA-Z0-9_.-] Character found in domain of location ["+domain+":"+path+"]");
|
||||
if(!isValidPath(path)) throw new IllegalArgumentException("Non [a-zA-Z0-9/_.-] Character found in path of location ["+domain+":"+path+"]");
|
||||
return new AssetLocation(domain, path);
|
||||
}
|
||||
|
||||
private static boolean isValidDomain(String s) {
|
||||
for(int i = 0,m=s.length();i<m;i++) {
|
||||
if(!isValidDomainChar(s.charAt(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isValidPath(String s) {
|
||||
for(int i = 0,m=s.length();i<m;i++) {
|
||||
if(!isValidPathChar(s.charAt(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isValidPathChar(char c) {
|
||||
return c == '/' || isValidDomainChar(c);
|
||||
}
|
||||
|
||||
private static boolean isValidDomainChar(char c) {
|
||||
return c == '_' || c == '-' || c == '.' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package speiger.src.coreengine.assets.base;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import speiger.src.collections.ints.functions.consumer.IntObjectConsumer;
|
||||
import speiger.src.collections.objects.collections.ObjectIterable;
|
||||
|
@ -37,6 +41,24 @@ public class MultiAsset implements Closeable, ObjectIterable<IAsset> {
|
|||
return assets.get(index);
|
||||
}
|
||||
|
||||
public <T> Iterable<T> map(AssetMapper<T> mappingFunction, Supplier<T> defaultValue) {
|
||||
return () -> new Iterator<T>() {
|
||||
int index = 0;
|
||||
int size = assets.size();
|
||||
@Override
|
||||
public boolean hasNext() { return index < size; }
|
||||
@Override
|
||||
public T next() {
|
||||
if(!hasNext()) throw new NoSuchElementException();
|
||||
try { return mappingFunction.apply(assets.get(index++)); }
|
||||
catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
return defaultValue.get();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super IAsset> action) {
|
||||
assets.forEach(action);
|
||||
|
@ -56,4 +78,8 @@ public class MultiAsset implements Closeable, ObjectIterable<IAsset> {
|
|||
public ObjectIterator<IAsset> iterator() {
|
||||
return assets.iterator();
|
||||
}
|
||||
|
||||
public static interface AssetMapper<T> {
|
||||
public T apply(IAsset asset) throws IOException;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ public class FolderAssetPackage implements IAssetPackage {
|
|||
@Override
|
||||
public void getModifiedTime(String domain, BiConsumer<AssetLocation, FileTime> accept) {
|
||||
try {
|
||||
Path start = baseFolder.resolve("assets");
|
||||
for(Path path : IterableWrapper.wrap(Files.walk(start.resolve(domain)).filter(Files::isRegularFile).iterator())) {
|
||||
Path start = baseFolder.resolve("assets/").resolve(domain);
|
||||
for(Path path : IterableWrapper.wrap(Files.walk(start).filter(Files::isRegularFile).iterator())) {
|
||||
accept.accept(AssetLocation.of(domain, start.relativize(path).toString().replace("\\", "/")), Files.getLastModifiedTime(path));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,39 @@
|
|||
package speiger.src.coreengine.rendering.gui.font;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import speiger.src.coreengine.assets.AssetLocation;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.GlythBaker;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData.GlythBaker;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.MissingGlyth;
|
||||
import speiger.src.coreengine.rendering.tesselation.buffer.IVertexBuilder;
|
||||
|
||||
public class Font {
|
||||
private static final AssetLocation DEFAULT = AssetLocation.of("default");
|
||||
|
||||
Map<AssetLocation, FontGroup> fonts;
|
||||
GlythBaker baker;
|
||||
float size;
|
||||
float oversample;
|
||||
GlythCache[] styledCache = new GlythCache[] {new GlythCache(this, 0), new GlythCache(this, 1), new GlythCache(this, 2), new GlythCache(this, 3)};
|
||||
MissingGlyth missingGlyth;
|
||||
|
||||
protected Font(Map<AssetLocation, FontGroup> fonts, GlythBaker baker, float size, float oversample) {
|
||||
protected Font(Map<AssetLocation, FontGroup> fonts, GlythBaker baker, float size, float oversample, Consumer<Runnable> clearing) {
|
||||
this.fonts = fonts;
|
||||
this.baker = baker;
|
||||
this.size = size;
|
||||
this.oversample = oversample;
|
||||
this.missingGlyth = new MissingGlyth(size, oversample * 2F);
|
||||
clearing.accept(this::reset);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
for(int i = 0;i<4;i++) {
|
||||
styledCache[i].reset();
|
||||
}
|
||||
missingGlyth.cleanCache();
|
||||
}
|
||||
|
||||
public GlythData data(AssetLocation font, int codepoint, int style) {
|
||||
|
@ -32,4 +47,39 @@ public class Font {
|
|||
protected FontGroup font(AssetLocation font) {
|
||||
return fonts.get(font);
|
||||
}
|
||||
|
||||
public void drawText(String text, float x, float y, int color, TexturedBuffer buffer) {
|
||||
int previousCodepoint = -1;
|
||||
for(int i = 0,m=text.length();i<m;) {
|
||||
int codepoint = text.codePointAt(i);
|
||||
GlythData data = data(DEFAULT, codepoint, 0);
|
||||
if(previousCodepoint != -1) {
|
||||
x += data.kernling(previousCodepoint);
|
||||
}
|
||||
Glyth glyth = glyth(DEFAULT, codepoint, 0);
|
||||
if(glyth.isValid()) {
|
||||
float minX = glyth.left() + x;
|
||||
float minY = glyth.top() + y;
|
||||
float maxX = glyth.right() + x;
|
||||
float maxY = glyth.bottom() + y;
|
||||
|
||||
IVertexBuilder builder = buffer.builderForTexture(glyth.texture());
|
||||
builder.pos(minX, minY, 0F).tex(glyth.minU(), glyth.maxV()).rgba(-1).endVertex();
|
||||
builder.pos(maxX, minY, 0F).tex(glyth.maxU(), glyth.maxV()).rgba(-1).endVertex();
|
||||
builder.pos(maxX, maxY, 0F).tex(glyth.maxU(), glyth.minV()).rgba(-1).endVertex();
|
||||
builder.pos(maxX, maxY, 0F).tex(glyth.maxU(), glyth.minV()).rgba(-1).endVertex();
|
||||
builder.pos(minX, maxY, 0F).tex(glyth.minU(), glyth.minV()).rgba(-1).endVertex();
|
||||
builder.pos(minX, minY, 0F).tex(glyth.minU(), glyth.maxV()).rgba(-1).endVertex();
|
||||
}
|
||||
x += data.advance();
|
||||
i += Character.charCount(codepoint);
|
||||
previousCodepoint = codepoint;
|
||||
}
|
||||
buffer.end();
|
||||
}
|
||||
|
||||
public static interface TexturedBuffer {
|
||||
public IVertexBuilder builderForTexture(int textureId);
|
||||
public void end();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,49 +2,29 @@ package speiger.src.coreengine.rendering.gui.font;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
|
||||
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
|
||||
import speiger.src.coreengine.assets.AssetLocation;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData;
|
||||
import speiger.src.coreengine.rendering.gui.font.providers.IFontProvider;
|
||||
|
||||
public class FontGroup {
|
||||
AssetLocation locations;
|
||||
List<IFontProvider> providers;
|
||||
StyledFont[] cache = new StyledFont[] {new StyledFont(0), new StyledFont(1), new StyledFont(2), new StyledFont(3)};
|
||||
|
||||
public FontGroup(AssetLocation locations, List<IFontProvider> providers) {
|
||||
this.locations = locations;
|
||||
this.providers = providers;
|
||||
}
|
||||
|
||||
public UnbakedGlyth unbaked(int codepoint, int style) {
|
||||
return cache[style & 0x3].unbaked(codepoint);
|
||||
|
||||
public GlythData data(int codepoint, int style, float size, float oversample) {
|
||||
for(int i = 0,m=providers.size();i<m;i++) {
|
||||
GlythData data = providers.get(i).glythData(codepoint, style, size, oversample);
|
||||
if(data != null) return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
providers.forEach(IFontProvider::close);
|
||||
providers.clear();
|
||||
}
|
||||
|
||||
private class StyledFont {
|
||||
final int style;
|
||||
Int2ObjectMap<UnbakedGlyth> dataGlyths = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public StyledFont(int style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public UnbakedGlyth unbaked(int codepoint) {
|
||||
return dataGlyths.computeIfAbsent(codepoint, this::compute);
|
||||
}
|
||||
|
||||
private UnbakedGlyth compute(int codepoint) {
|
||||
for(int i = 0,m=providers.size();i<m;i++) {
|
||||
UnbakedGlyth data = providers.get(i).glythData(codepoint, style);
|
||||
if(data != null) return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,39 +2,82 @@ package speiger.src.coreengine.rendering.gui.font;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||
import speiger.src.collections.objects.lists.ObjectList;
|
||||
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
|
||||
import speiger.src.coreengine.assets.AssetFilter;
|
||||
import speiger.src.coreengine.assets.AssetLocation;
|
||||
import speiger.src.coreengine.assets.base.IAsset;
|
||||
import speiger.src.coreengine.assets.base.IAssetProvider;
|
||||
import speiger.src.coreengine.assets.base.MultiAsset;
|
||||
import speiger.src.coreengine.assets.base.SteppedReloadableAsset;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo;
|
||||
import speiger.src.coreengine.rendering.gui.font.providers.IFontProvider;
|
||||
import speiger.src.coreengine.rendering.gui.font.providers.STBTrueTypeProvider;
|
||||
import speiger.src.coreengine.utils.helpers.JsonUtil;
|
||||
|
||||
public class FontManager extends SteppedReloadableAsset<Map<AssetLocation, List<IFontProvider>>> {
|
||||
public class FontManager extends SteppedReloadableAsset<Map<AssetLocation, ObjectList<IFontProvider>>> {
|
||||
private static final int TEXTURE_SIZE = 2048;
|
||||
private static final AssetFilter FILTER = AssetFilter.json("font");
|
||||
Map<AssetLocation, FontGroup> fonts = Object2ObjectMap.builder().linkedMap();
|
||||
Map<String, BiFunction<JsonObject, IAssetProvider, IFontProvider>> fontParsers = Object2ObjectMap.builder().map();
|
||||
List<Runnable> listeners = new ObjectArrayList<>();
|
||||
List<FontTexture> textures = new ObjectArrayList<>();
|
||||
|
||||
public FontManager() {
|
||||
registerParser("stb-ttf", STBTrueTypeProvider::create);
|
||||
}
|
||||
|
||||
public void registerParser(String id, BiFunction<JsonObject, IAssetProvider, IFontProvider> parser) {
|
||||
fontParsers.putIfAbsent(id, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return null; }
|
||||
|
||||
public String getName() { return "Font Manager"; }
|
||||
|
||||
@Override
|
||||
protected Map<AssetLocation, ObjectList<IFontProvider>> prepare(IAssetProvider provider) {
|
||||
Map<AssetLocation, IFontProvider> loadingCache = Object2ObjectMap.builder().linkedMap();
|
||||
Object2ObjectMap<AssetLocation, ObjectList<IFontProvider>> providers = Object2ObjectMap.builder().linkedMap();
|
||||
for(Entry<AssetLocation, MultiAsset> entry : FILTER.multiRoot(provider).entrySet()) {
|
||||
AssetLocation id = entry.getKey();
|
||||
for(JsonObject obj : entry.getValue().map(IAsset::json, JsonObject::new)) {
|
||||
JsonUtil.iterateValues(obj.get("providers"), T -> {
|
||||
AssetLocation location = AssetLocation.tryOf(T.getAsString());
|
||||
if(location == null || !location.isInFolder()) return;
|
||||
IFontProvider font = loadingCache.computeIfAbsent(location.prefix("font"), E -> getFont(E, provider));
|
||||
if(font == null) return;
|
||||
providers.supplyIfAbsent(FILTER.id(id), ObjectArrayList::new).add(font);
|
||||
});
|
||||
}
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(Map<AssetLocation, ObjectList<IFontProvider>> value, IAssetProvider provider) {
|
||||
reset();
|
||||
value.forEach((K, V) -> fonts.put(K, new FontGroup(K, V)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
reset();
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<AssetLocation, List<IFontProvider>> prepare(IAssetProvider provider) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(Map<AssetLocation, List<IFontProvider>> value, IAssetProvider provider) {
|
||||
|
||||
private void reset() {
|
||||
listeners.forEach(Runnable::run);
|
||||
textures.forEach(FontTexture::delete);
|
||||
textures.clear();
|
||||
fonts.values().forEach(FontGroup::close);
|
||||
fonts.clear();
|
||||
}
|
||||
|
||||
public Font createFont(float size) {
|
||||
|
@ -42,7 +85,7 @@ public class FontManager extends SteppedReloadableAsset<Map<AssetLocation, List<
|
|||
}
|
||||
|
||||
public Font createFont(float size, float oversample) {
|
||||
return new Font(fonts, this::stitch, size, oversample);
|
||||
return new Font(fonts, this::stitch, size, oversample, listeners::add);
|
||||
}
|
||||
|
||||
private Glyth stitch(IGlythSheetInfo info) {
|
||||
|
@ -55,4 +98,16 @@ public class FontManager extends SteppedReloadableAsset<Map<AssetLocation, List<
|
|||
Glyth glyth = texture.build(info);
|
||||
return glyth;
|
||||
}
|
||||
|
||||
private IFontProvider getFont(AssetLocation location, IAssetProvider provider) {
|
||||
try(IAsset asset = provider.getAsset(location)) {
|
||||
JsonObject obj = asset.json();
|
||||
BiFunction<JsonObject, IAssetProvider, IFontProvider> builder = fontParsers.get(obj.get("type").getAsString());
|
||||
return builder == null ? null : builder.apply(obj, provider);
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package speiger.src.coreengine.rendering.gui.font;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import speiger.src.coreengine.assets.base.IAssetProvider;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo;
|
||||
|
@ -43,7 +45,11 @@ public class FontTexture extends BaseTexture {
|
|||
createTexture();
|
||||
TextureMetadata metadata = metadataByColor(color);
|
||||
metadata.applyArguments(textureType());
|
||||
GLFunctions.upload2DImage(textureType(), 0, metadata, bounds, bounds, 0, 0L);
|
||||
long allocate = MemoryUtil.nmemAlloc(bounds * bounds * formatByColor(color).components());
|
||||
ITextureComponentFormat format = formatByColor(color);
|
||||
GLFunctions.upload2DImage(textureType(), 0, format, bounds, bounds, 0, format, GLDataType.UNSIGNED_BYTE, allocate);
|
||||
// GLFunctions.upload2DImage(textureType(), 0, metadata, bounds, bounds, 0, allocate);
|
||||
MemoryUtil.nmemFree(allocate);
|
||||
}
|
||||
|
||||
public Glyth build(IGlythSheetInfo info) {
|
||||
|
|
|
@ -19,6 +19,10 @@ public class GlythCache {
|
|||
this.style = style;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
private FontCache cache(AssetLocation font) {
|
||||
return cache.computeIfAbsent(font, FontCache::new);
|
||||
}
|
||||
|
@ -39,7 +43,7 @@ public class GlythCache {
|
|||
public FontCache(AssetLocation fontId) {
|
||||
this.fontId = fontId;
|
||||
}
|
||||
|
||||
|
||||
public GlythData data(int codepoint) {
|
||||
return data.computeIfAbsent(codepoint, this::compute);
|
||||
}
|
||||
|
@ -49,12 +53,12 @@ public class GlythCache {
|
|||
}
|
||||
|
||||
private GlythData compute(int codepoint) {
|
||||
return new GlythData(font.font(fontId).unbaked(codepoint, style), font.size);
|
||||
GlythData data = font.font(fontId).data(codepoint, style, font.size, font.oversample);
|
||||
return data == null ? font.missingGlyth : data;
|
||||
}
|
||||
|
||||
private Glyth bake(int codepoint) {
|
||||
return font.font(fontId).unbaked(codepoint, style).bake(font.baker, font.size, font.oversample);
|
||||
return data(codepoint).bake(font.baker);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -10,4 +10,6 @@ public record Glyth(int texture, float minU, float minV, float maxU, float maxV,
|
|||
public Glyth(int texture, float minU, float minV, float maxU, float maxV) {
|
||||
this(texture, minU, minV, maxU, maxV, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public boolean isValid() { return texture != -1; }
|
||||
}
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
package speiger.src.coreengine.rendering.gui.font.glyth;
|
||||
|
||||
import speiger.src.collections.ints.functions.function.Int2FloatFunction;
|
||||
import speiger.src.collections.ints.maps.impl.hash.Int2FloatOpenHashMap;
|
||||
import speiger.src.collections.ints.maps.interfaces.Int2FloatMap;
|
||||
|
||||
public class GlythData {
|
||||
float advance;
|
||||
float shadowOffset;
|
||||
Int2FloatMap kernlings = new Int2FloatOpenHashMap();
|
||||
Int2FloatFunction kernling;
|
||||
public interface GlythData {
|
||||
public float advance();
|
||||
public float kernling(int codepoint);
|
||||
public default float shadowOffset() { return 1F; }
|
||||
public Glyth bake(GlythBaker baker);
|
||||
|
||||
public GlythData(float advance, float shadowOffset, Int2FloatFunction kernling) {
|
||||
this.advance = advance;
|
||||
this.shadowOffset = shadowOffset;
|
||||
this.kernling = kernling;
|
||||
|
||||
public static record EmptyGlythData(float advance) implements GlythData {
|
||||
@Override
|
||||
public Glyth bake(GlythBaker baker) { return Glyth.EMPTY; }
|
||||
@Override
|
||||
public float kernling(int codepoint) { return 0F; }
|
||||
}
|
||||
|
||||
public GlythData(UnbakedGlyth glyth, float size) {
|
||||
this(glyth.advance(size), glyth.shadowOffset(), glyth::kernling);
|
||||
public static interface GlythBaker {
|
||||
Glyth bake(IGlythSheetInfo info);
|
||||
}
|
||||
|
||||
public float advance() { return advance; }
|
||||
public float shadowOffset() { return shadowOffset; }
|
||||
public float kernling(int codepoint) { return kernlings.computeFloatIfAbsent(codepoint, kernling); }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package speiger.src.coreengine.rendering.gui.font.glyth;
|
||||
|
||||
import speiger.src.collections.ints.functions.function.Int2FloatFunction;
|
||||
import speiger.src.collections.ints.maps.impl.hash.Int2FloatOpenHashMap;
|
||||
import speiger.src.collections.ints.maps.interfaces.Int2FloatMap;
|
||||
|
||||
public class GlythDataOld {
|
||||
float advance;
|
||||
float shadowOffset;
|
||||
Int2FloatMap kernlings = new Int2FloatOpenHashMap();
|
||||
Int2FloatFunction kernling;
|
||||
|
||||
public GlythDataOld(float advance, float shadowOffset, Int2FloatFunction kernling) {
|
||||
this.advance = advance;
|
||||
this.shadowOffset = shadowOffset;
|
||||
this.kernling = kernling;
|
||||
}
|
||||
|
||||
public GlythDataOld(GlythData glyth) {
|
||||
this(glyth.advance(), glyth.shadowOffset(), glyth::kernling);
|
||||
}
|
||||
|
||||
public float advance() { return advance; }
|
||||
public float shadowOffset() { return shadowOffset; }
|
||||
public float kernling(int codepoint) { return kernlings.computeFloatIfAbsent(codepoint, kernling); }
|
||||
}
|
|
@ -3,6 +3,15 @@ package speiger.src.coreengine.rendering.gui.font.glyth;
|
|||
public interface IGlythSheetInfo {
|
||||
public int width();
|
||||
public int height();
|
||||
public default float xOffset() { return 0F; }
|
||||
public default float yOffset() { return 3F; }
|
||||
public float oversample();
|
||||
public boolean isColored();
|
||||
public void upload(int x, int y);
|
||||
|
||||
public default float left() { return xOffset(); }
|
||||
public default float right() { return xOffset() + width() / oversample(); }
|
||||
public default float top() { return yOffset(); }
|
||||
public default float bottom() { return yOffset() + height() / oversample(); }
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package speiger.src.coreengine.rendering.gui.font.glyth;
|
||||
|
||||
import speiger.src.coreengine.math.MathUtils;
|
||||
import speiger.src.coreengine.rendering.gui.font.FontTexture;
|
||||
import speiger.src.coreengine.rendering.textures.custom.Drawable;
|
||||
|
||||
public class MissingGlyth implements GlythData {
|
||||
private static final int WIDTH = 10;
|
||||
private static final int HEIGHT = 57;
|
||||
float size;
|
||||
float oversample;
|
||||
float advance;
|
||||
float scale;
|
||||
int width;
|
||||
int height;
|
||||
Glyth cached;
|
||||
|
||||
public MissingGlyth(float size, float oversample) {
|
||||
this.size = size;
|
||||
this.oversample = oversample;
|
||||
this.scale = (size * oversample) / HEIGHT;
|
||||
this.advance = WIDTH * (size / HEIGHT);
|
||||
this.width = (int)(WIDTH * scale);
|
||||
this.height = (int)(HEIGHT * scale);
|
||||
}
|
||||
|
||||
public void cleanCache() {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float advance() { return advance; }
|
||||
@Override
|
||||
public float kernling(int codepoint) { return 0; }
|
||||
@Override
|
||||
public Glyth bake(GlythBaker baker) {
|
||||
if(cached == null) {
|
||||
cached = baker.bake(new IGlythSheetInfo() {
|
||||
@Override
|
||||
public int width() { return width; }
|
||||
@Override
|
||||
public int height() { return height; }
|
||||
@Override
|
||||
public void upload(int x, int y) {
|
||||
Drawable drawable = new Drawable(FontTexture.formatByColor(false), width, height);
|
||||
draw(drawable);
|
||||
drawable.upload(x, y, 0, 0, width, height);
|
||||
drawable.close();
|
||||
}
|
||||
@Override
|
||||
public boolean isColored() { return false; }
|
||||
@Override
|
||||
public float oversample() { return oversample; }
|
||||
});
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
private void draw(Drawable texture) {
|
||||
texture.fill(0, 0, width, height, 255);
|
||||
int offset = MathUtils.ceil(width / 7.5F);
|
||||
texture.fill(offset, offset, width - offset * 2, height - offset * 2, 0);
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package speiger.src.coreengine.rendering.gui.font.glyth;
|
||||
|
||||
public interface UnbakedGlyth {
|
||||
public float advance(float size);
|
||||
public float kernling(int codepoint);
|
||||
public default float shadowOffset() { return 1F; }
|
||||
public Glyth bake(GlythBaker baker, float size, float oversample);
|
||||
|
||||
|
||||
public static record EmptyGlythData(float advance) implements UnbakedGlyth {
|
||||
@Override
|
||||
public Glyth bake(GlythBaker baker, float size, float oversample) { return Glyth.EMPTY; }
|
||||
@Override
|
||||
public float advance(float size) { return advance; }
|
||||
@Override
|
||||
public float kernling(int codepoint) { return 0F; }
|
||||
}
|
||||
|
||||
public static interface GlythBaker {
|
||||
Glyth bake(IGlythSheetInfo info);
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package speiger.src.coreengine.rendering.gui.font.providers;
|
||||
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth;
|
||||
|
||||
public class BitmapProivder implements IFontProvider {
|
||||
|
||||
|
||||
@Override
|
||||
public UnbakedGlyth glythData(int codepoint, int style) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
private static class BitmapGlyth implements UnbakedGlyth {
|
||||
int minX;
|
||||
int minY;
|
||||
int maxX;
|
||||
int maxY;
|
||||
int glyth;
|
||||
|
||||
@Override
|
||||
public Glyth bake(GlythBaker baker, float size, float oversample) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float advance(float size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float kernling(int codepoint) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,14 @@
|
|||
package speiger.src.coreengine.rendering.gui.font.providers;
|
||||
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData;
|
||||
|
||||
public interface IFontProvider extends AutoCloseable {
|
||||
public interface IFontProvider {
|
||||
public static final int REGULAR = 0;
|
||||
public static final int BOLD = 1;
|
||||
public static final int ITALIC = 2;
|
||||
public static final int ITALIC_BOLD = 3;
|
||||
|
||||
|
||||
public UnbakedGlyth glythData(int codepoint, int style);
|
||||
@Override
|
||||
void close();
|
||||
public GlythData glythData(int codepoint, int style, float size, float oversample);
|
||||
public void close();
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@ import speiger.src.coreengine.assets.base.IAssetProvider;
|
|||
import speiger.src.coreengine.assets.parsers.NativeMemoryParser;
|
||||
import speiger.src.coreengine.rendering.gui.font.FontTexture;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.EmptyGlythData;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData.EmptyGlythData;
|
||||
import speiger.src.coreengine.rendering.gui.font.glyth.IGlythSheetInfo;
|
||||
import speiger.src.coreengine.rendering.textures.custom.Drawable;
|
||||
import speiger.src.coreengine.rendering.utils.values.textures.GLTextureFormat;
|
||||
import speiger.src.coreengine.rendering.utils.GLStateTracker;
|
||||
import speiger.src.coreengine.utils.helpers.JsonUtil;
|
||||
|
||||
public class STBTrueTypeProvider implements IFontProvider {
|
||||
|
@ -53,7 +53,6 @@ public class STBTrueTypeProvider implements IFontProvider {
|
|||
private static TrueTypeInstance create(int style, JsonObject obj, IAssetProvider provider) {
|
||||
if(obj == null || !obj.has("file")) return null;
|
||||
AssetLocation location = AssetLocation.of(obj.get("file").getAsString());
|
||||
float size = JsonUtil.getOrDefault(obj, "size", 24F);
|
||||
float oversample = JsonUtil.getOrDefault(obj, "oversample", 1F);
|
||||
float shadowOffset = JsonUtil.getOrDefault(obj, "shadowOffset", 1F);
|
||||
float xOff = 0;
|
||||
|
@ -74,7 +73,7 @@ public class STBTrueTypeProvider implements IFontProvider {
|
|||
info.free();
|
||||
return null;
|
||||
}
|
||||
return new TrueTypeInstance(style, MemoryUtil.memAddress(buffer), info, size, oversample, xOff, yOff, shadowOffset, builder.toString());
|
||||
return new TrueTypeInstance(style, MemoryUtil.memAddress(buffer), info, oversample, xOff, yOff, shadowOffset, builder.toString());
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -91,10 +90,9 @@ public class STBTrueTypeProvider implements IFontProvider {
|
|||
final float xOff;
|
||||
final float yOff;
|
||||
final float shadowOffset;
|
||||
final float pointScale;
|
||||
final float ascent;
|
||||
|
||||
public TrueTypeInstance(int style, long data, STBTTFontinfo info, float size, float oversample, float xOff, float yOff, float shadowOffset, String skip) {
|
||||
public TrueTypeInstance(int style, long data, STBTTFontinfo info, float oversample, float xOff, float yOff, float shadowOffset, String skip) {
|
||||
this.style = style;
|
||||
this.data = data;
|
||||
this.info = info;
|
||||
|
@ -103,10 +101,9 @@ public class STBTrueTypeProvider implements IFontProvider {
|
|||
this.xOff = xOff;
|
||||
this.yOff = yOff;
|
||||
this.shadowOffset = shadowOffset;
|
||||
pointScale = STBTruetype.stbtt_ScaleForPixelHeight(info, size * oversample);
|
||||
int[] ascent = new int[1];
|
||||
STBTruetype.stbtt_GetFontVMetrics(info, ascent, new int[1], new int[1]);
|
||||
this.ascent = pointScale * ascent[0];
|
||||
this.ascent = ascent[0];
|
||||
}
|
||||
|
||||
public void free() {
|
||||
|
@ -116,37 +113,39 @@ public class STBTrueTypeProvider implements IFontProvider {
|
|||
data = 0L;
|
||||
}
|
||||
|
||||
public UnbakedGlyth glythData(int codepoint) {
|
||||
public GlythData glythData(int codepoint, float size, float oversample) {
|
||||
if(skip.contains(codepoint)) return null;
|
||||
int glyth = STBTruetype.nstbtt_FindGlyphIndex(info.address(), codepoint);
|
||||
if(glyth == 0) return null;
|
||||
oversample *= this.oversample;
|
||||
float scale = STBTruetype.stbtt_ScaleForPixelHeight(info, size * oversample);
|
||||
try(MemoryStack stack = MemoryStack.stackPush()) {
|
||||
IntBuffer left = stack.mallocInt(1);
|
||||
IntBuffer bottom = stack.mallocInt(1);
|
||||
IntBuffer right = stack.mallocInt(1);
|
||||
IntBuffer top = stack.mallocInt(1);
|
||||
IntBuffer advance = stack.mallocInt(1);
|
||||
IntBuffer leftSideBearing = stack.mallocInt(1);
|
||||
IntBuffer leftSideBearing = stack.mallocInt(1);
|
||||
STBTruetype.stbtt_GetGlyphHMetrics(info, glyth, advance, leftSideBearing);
|
||||
STBTruetype.stbtt_GetGlyphBitmapBoxSubpixel(info, glyth, pointScale, pointScale, xOff, yOff, left, bottom, right, top);
|
||||
STBTruetype.stbtt_GetGlyphBitmapBoxSubpixel(info, glyth, scale, scale, xOff, yOff, left, bottom, right, top);
|
||||
int minX = left.get(0);
|
||||
int minY = -top.get(0);
|
||||
int maxX = right.get(0);
|
||||
int maxY = -bottom.get(0);
|
||||
if(maxX - minX <= 0 || maxY - minY <= 0) return new EmptyGlythData(advance.get(0) * pointScale / oversample);
|
||||
return new STBGlyth(this, minX, minY, maxX, maxY, advance.get(0) * pointScale, leftSideBearing.get(0) * pointScale, glyth);
|
||||
if(maxX - minX <= 0 || maxY - minY <= 0) return new EmptyGlythData(advance.get(0) * scale / oversample);
|
||||
return new STBGlyth(this, minX, minY, maxX, maxY, advance.get(0), leftSideBearing.get(0), scale, oversample, glyth);
|
||||
}
|
||||
catch(Throwable exception) {
|
||||
exception.printStackTrace();
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedGlyth glythData(int codepoint, int style) {
|
||||
public GlythData glythData(int codepoint, int style, float size, float oversample) {
|
||||
TrueTypeInstance instance = instances[style & 0x3];
|
||||
return instance == null ? null : instance.glythData(codepoint);
|
||||
return instance == null ? null : instance.glythData(codepoint, size, oversample);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,48 +158,56 @@ public class STBTrueTypeProvider implements IFontProvider {
|
|||
instances = null;
|
||||
}
|
||||
|
||||
private static class STBGlyth implements UnbakedGlyth {
|
||||
private static class STBGlyth implements GlythData {
|
||||
final TrueTypeInstance owner;
|
||||
final float xOffset;
|
||||
final float yOffset;
|
||||
final int width;
|
||||
final int height;
|
||||
final float oversample;
|
||||
final float scale;
|
||||
final float advance;
|
||||
final int glyth;
|
||||
|
||||
public STBGlyth(TrueTypeInstance owner, int minX, int minY, int maxX, int maxY, float advance, float leftPadding, int glyth) {
|
||||
public STBGlyth(TrueTypeInstance owner, int minX, int minY, int maxX, int maxY, float advance, float leftPadding, float scale, float oversample, int glyth) {
|
||||
this.owner = owner;
|
||||
this.width = maxX - minX;
|
||||
this.height = maxY - minY;
|
||||
this.advance = advance / owner.oversample;
|
||||
this.scale = scale;
|
||||
this.oversample = oversample;
|
||||
this.xOffset = ((leftPadding * scale) + minX + owner.xOff) / oversample;
|
||||
this.yOffset = ((owner.ascent * scale) - maxY + owner.yOff) / oversample;
|
||||
this.advance = advance * scale / oversample;
|
||||
this.glyth = glyth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float advance(float size) { return advance; }
|
||||
public float advance() { return advance; }
|
||||
@Override
|
||||
public float shadowOffset() { return owner.shadowOffset; }
|
||||
@Override
|
||||
public float kernling(int codepoint) { return STBTruetype.stbtt_GetCodepointKernAdvance(owner.info, codepoint, glyth); }
|
||||
public float kernling(int codepoint) { return STBTruetype.stbtt_GetCodepointKernAdvance(owner.info, codepoint, glyth) * scale / oversample; }
|
||||
@Override
|
||||
public Glyth bake(GlythBaker baker, float size, float oversample) {
|
||||
public Glyth bake(GlythBaker baker) {
|
||||
return baker.bake(new IGlythSheetInfo() {
|
||||
@Override
|
||||
public float xOffset() { return xOffset; }
|
||||
@Override
|
||||
public float yOffset() { return yOffset; }
|
||||
@Override
|
||||
public int width() { return width; }
|
||||
@Override
|
||||
public int height() { return height; }
|
||||
@Override
|
||||
public float oversample() { return oversample; }
|
||||
@Override
|
||||
public boolean isColored() { return false; }
|
||||
@Override
|
||||
public void upload(int x, int y) {
|
||||
System.out.println("Testing: "+GLStateTracker.instance().textures.getTextureId(0));
|
||||
Drawable drawable = new Drawable(FontTexture.formatByColor(false), width, height);
|
||||
drawable.drawFont(owner.info, glyth, owner.xOff, owner.yOff, 0, 0, width, height, owner.pointScale, owner.pointScale);
|
||||
Drawable upload = new Drawable(GLTextureFormat.RGBA, width, height);
|
||||
int pattern = Integer.divideUnsigned(-1, 255);
|
||||
for(int i = 0,m=width*height;i<m;i++) {
|
||||
upload.set(i, drawable.getR(i) * pattern);
|
||||
}
|
||||
|
||||
upload.upload(x, y, 0, 0, width, height);
|
||||
upload.close();
|
||||
drawable.drawFont(owner.info, glyth, owner.xOff, owner.yOff, 0, 0, width, height, scale, scale);
|
||||
drawable.upload(x, y, 0, 0, width, height);
|
||||
drawable.close();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -129,9 +129,18 @@ public class Drawable implements IDrawable, AutoCloseable {
|
|||
|
||||
@Override
|
||||
public void fill(int x, int y, int width, int height, int data) {
|
||||
if(components != 4) throw new IllegalStateException("Format has to be 4 components");
|
||||
if(components != 4 && components != 1) throw new IllegalStateException("Format has to be 1 or 4 components");
|
||||
ensureValid(x, y);
|
||||
ensureValid(x+width, y+height);
|
||||
if(components == 1) {
|
||||
for(int xOff = 0;xOff<width;xOff++) {
|
||||
for(int yOff = 0;yOff<height;yOff++) {
|
||||
MemoryUtil.memPutByte(this.pixels + offset(x+xOff, y+yOff), (byte)(data & 0xFF));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for(int xOff = 0;xOff<width;xOff++) {
|
||||
for(int yOff = 0;yOff<height;yOff++) {
|
||||
MemoryUtil.memPutInt(this.pixels + offset(x+xOff, y+yOff), data);
|
||||
|
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"providers": [
|
||||
"base:roboto/font.json"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#version 330
|
||||
|
||||
in vec4 pass_color;
|
||||
in vec2 pass_tex;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = pass_color * texture2D(texture, pass_tex);
|
||||
if(color.a < 0.2) {
|
||||
discard;
|
||||
}
|
||||
frag_color = color;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#version 330
|
||||
|
||||
layout(location = 0) in vec3 in_position;
|
||||
layout(location = 1) in vec2 in_tex;
|
||||
layout(location = 2) in vec4 in_color;
|
||||
|
||||
out vec4 pass_color;
|
||||
out vec2 pass_tex;
|
||||
|
||||
uniform mat4 proViewMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = proViewMatrix * vec4(in_position, 1.0);
|
||||
pass_color = in_color;
|
||||
pass_tex = in_tex;
|
||||
}
|
|
@ -9,5 +9,9 @@ uniform sampler2D texture;
|
|||
|
||||
void main()
|
||||
{
|
||||
frag_color = pass_color * texture2D(texture, pass_tex);
|
||||
vec4 color = pass_color * texture2D(texture, pass_tex);
|
||||
if(color.a < 0.2) {
|
||||
discard;
|
||||
}
|
||||
frag_color = color;
|
||||
}
|
Loading…
Reference in New Issue