Reworked a couple systems.

-Added: STBImage support.
-Added: Dynamic Font Loader that supports Bitmap/TTF fonts (ttf using java not STB, because small deadline)
-Added: NativeMemory Loader as optional parser into asset loading.
-Reworked: How Images are made reloadable.
-Added: A dynamic AtlasBuilder
This commit is contained in:
Speiger 2021-09-28 01:35:07 +02:00
parent 9c947c4898
commit 4763e62b53
56 changed files with 1976 additions and 653 deletions

View File

@ -64,5 +64,5 @@ dependencies {
compile 'com.google.code.gson:gson:2.8.6'
//Primitive Collections
compile 'de.speiger:Primitive-Collections:0.3.5'
compile 'de.speiger:Primitive-Collections:0.4.0'
}

View File

@ -1,6 +1,7 @@
package speiger.src.coreengine.application;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.function.IntConsumer;
import java.util.function.ObjLongConsumer;
@ -11,6 +12,7 @@ import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.reloader.ResourceReloader;
import speiger.src.coreengine.rendering.gui.GuiManager;
import speiger.src.coreengine.rendering.gui.base.DebugOverlay;
import speiger.src.coreengine.rendering.gui.renderer.provider.FontManager;
import speiger.src.coreengine.rendering.input.Keyboard;
import speiger.src.coreengine.rendering.input.Mouse;
import speiger.src.coreengine.rendering.input.camera.Camera;
@ -18,7 +20,8 @@ import speiger.src.coreengine.rendering.input.window.Window;
import speiger.src.coreengine.rendering.input.window.WindowProvider;
import speiger.src.coreengine.rendering.shader.ProjectionBuffer;
import speiger.src.coreengine.rendering.shader.ShaderTracker;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.NativeMemoryParser;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.Cursor;
import speiger.src.coreengine.utils.counters.timers.FPSTimer;
import speiger.src.coreengine.utils.eventbus.EventBus;
@ -41,6 +44,7 @@ public abstract class Application
protected ResourceReloader reloader = new ResourceReloader();
protected AssetManager assetManager;
protected FontManager fonts = new FontManager();
protected ProjectionBuffer projectionBuffer;
protected GuiManager uiManager;
@ -49,10 +53,11 @@ public abstract class Application
GLFWErrorCallback.createPrint(System.err).set();
if(!GLFW.glfwInit()) throw new IllegalStateException("OpenGL can't be loaded");
provider.init();
boolean initEarly = earlyUILoad();
try
{
mainWindow = createWindow(provider);
mainWindow.finishWindow();
if(initEarly) mainWindow.finishWindow();
}
catch(Exception e)
{
@ -68,6 +73,7 @@ public abstract class Application
file = file.getName().endsWith(".jar") ? file : new File("bin/main");
internalInit(file);
init(file);
if(!initEarly) mainWindow.finishWindow();
executor.start(mainWindow);
mainWindow.destroy();
reloader.deleteResources();
@ -78,8 +84,12 @@ public abstract class Application
protected void internalInit(File file)
{
assetManager = reloader.addReloadableResource(new AssetManager(file), true);
assetManager.registerAssetParser(ByteBuffer.class, new NativeMemoryParser());
reloader.addReloadableResource(fonts);
fonts.setAssetManager(assetManager);
ShaderTracker.INSTANCE.init(assetManager);
TextureManager.INSTANCE.init(assetManager);
preinit();
reloader.addReloadableResource(ShaderTracker.INSTANCE);
reloader.addReloadableResource(TextureManager.INSTANCE);
camera = new Camera(mainWindow);
@ -95,8 +105,10 @@ public abstract class Application
public void addExtraTickRates(IntConsumer ticks) {};
public void addExtraTimers(ObjLongConsumer<String> profiler) {};
public boolean initUI() { return true; }
public boolean earlyUILoad() { return true; }
public DebugOverlay createCustomDebug() { return null; }
public abstract Window createWindow(WindowProvider provider) throws Exception;
public void preinit() {}
public abstract void init(File file);
public abstract void update();
public abstract void render(float particalTicks);

View File

@ -13,7 +13,7 @@ public class BaseUIManager extends GuiManager
public BaseUIManager(Application application)
{
super(application.mainWindow, application.eventBus);
super(application.mainWindow, application.eventBus, application.fonts);
this.application = application;
}

View File

@ -7,9 +7,9 @@ import java.util.function.Function;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
public final class AssetLocation
public final class AssetLocation implements Comparable<AssetLocation>
{
static final Map<String, AssetLocation> LOCATION = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<String, AssetLocation>());
static final Map<String, AssetLocation> LOCATION = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>());
static final Function<String, AssetLocation> BUILDER = AssetLocation::compute;
final String domain;
final String location;
@ -74,6 +74,13 @@ public final class AssetLocation
return false;
}
@Override
public int compareTo(AssetLocation o)
{
int result = domain.compareToIgnoreCase(o.domain);
return result != 0 ? result : location.compareToIgnoreCase(location);
}
public boolean matches(AssetLocation location)
{
return location.domain.equals(domain) && location.location.equals(this.location);

View File

@ -5,21 +5,25 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.zip.ZipFile;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry;
import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet;
import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
import speiger.src.coreengine.assets.impl.FolderAssetPackage;
import speiger.src.coreengine.assets.impl.ZipAssetPackage;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
public class AssetManager implements IReloadableResource
{
Map<String, DomainAssets> domains = new Object2ObjectLinkedOpenHashMap<>();
Object2ObjectMap<String, DomainAssets> domains = new Object2ObjectLinkedOpenHashMap<>();
Object2ObjectMap<Class<?>, IAssetParser<?>> parsers = new Object2ObjectLinkedOpenHashMap<>();
Path path;
@ -121,11 +125,21 @@ public class AssetManager implements IReloadableResource
DomainAssets asset = domains.get(location.getDomain());
if(asset == null)
{
throw new FileNotFoundException("File["+location.toString()+"] not found");
throw new FileNotFoundException("Domain & File["+location.toString()+"] not found");
}
return asset.getAllAssets(location);
}
public Collection<AssetLocation> gatherAssets(String location, int maxDepth, Predicate<String> filter)
{
Set<AssetLocation> result = new ObjectLinkedOpenHashSet<>();
for(Entry<String, DomainAssets> asset : Object2ObjectMaps.fastIterable(domains))
{
asset.getValue().gatherAssets(AssetLocation.of(asset.getKey(), location), maxDepth, filter, result);
}
return result;
}
protected boolean isZipFile(Path file)
{
try(ZipFile zip = new ZipFile(file.toFile())) { return true; }
@ -185,5 +199,13 @@ public class AssetManager implements IReloadableResource
}
return new MultiAsset(assets.toArray(new IAsset[assets.size()]));
}
public void gatherAssets(AssetLocation folder, int maxDepth, Predicate<String> filter, Collection<AssetLocation> result)
{
for(IAssetPackage entry : packages)
{
entry.getAllAssets(folder, filter, maxDepth, result);
}
}
}
}

View File

@ -5,6 +5,7 @@ import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import com.google.gson.JsonObject;
@ -16,11 +17,13 @@ public interface IAsset extends Closeable
public InputStream getStream() throws IOException;
public BufferedImage getTexture() throws Exception;
public ByteBuffer getBytes() throws IOException;
public BufferedReader getStringReader() throws IOException;
public JsonObject getJsonObject() throws IOException;
public BufferedImage getTexture() throws Exception;
public <T> T getCustom(Class<T> clz) throws IOException;
}

View File

@ -1,11 +1,14 @@
package speiger.src.coreengine.assets;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public interface IAssetPackage
{
public void setParsers(Function<Class<?>, IAssetParser<?>> parsers);
public List<String> getDomains();
public IAsset getAsset(AssetLocation location);
public void getAllAssets(AssetLocation folder, Predicate<String> fileNames, int maxDepth, Collection<AssetLocation> result);
}

View File

@ -1,10 +1,11 @@
package speiger.src.coreengine.assets;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.function.Consumer;
public interface IAssetParser<T>
{
public T parseAsset(Path path, Consumer<Closeable> autoCloser);
public T parseAsset(Path path, Consumer<Closeable> autoCloser) throws IOException;
}

View File

@ -5,6 +5,7 @@ import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@ -76,6 +77,12 @@ public class FolderAsset implements IAsset
return markClosed(Files.newInputStream(path));
}
@Override
public ByteBuffer getBytes() throws IOException
{
return ByteBuffer.wrap(Files.readAllBytes(path));
}
@Override
public BufferedImage getTexture() throws Exception
{

View File

@ -5,14 +5,18 @@ import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.IAssetPackage;
import speiger.src.coreengine.assets.IAssetParser;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
public class FolderAssetPackage implements IAssetPackage
{
@ -59,4 +63,22 @@ public class FolderAssetPackage implements IAssetPackage
Path path = baseFolder.resolve(location.getActualLocation());
return Files.exists(path) ? new FolderAsset(location, path, parsers) : null;
}
@Override
public void getAllAssets(AssetLocation folder, Predicate<String> fileNames, int maxDepth, Collection<AssetLocation> result)
{
Path start = baseFolder.resolve(folder.getActualLocation());
if(Files.notExists(start)) return;
try(Stream<Path> stream = Files.walk(start, maxDepth).filter(Files::isRegularFile).filter(T -> fileNames.test(T.getFileName().toString())))
{
for(Path path : IterableWrapper.wrap(stream.iterator()))
{
result.add(folder.subAsset(start.relativize(path).toString()));
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}

View File

@ -4,6 +4,7 @@ import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Function;
@ -71,6 +72,12 @@ public class ZipAsset implements IAsset
return Files.newInputStream(path);
}
@Override
public ByteBuffer getBytes() throws IOException
{
return ByteBuffer.wrap(Files.readAllBytes(path));
}
@Override
public BufferedImage getTexture() throws Exception
{

View File

@ -7,15 +7,19 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.IAssetPackage;
import speiger.src.coreengine.assets.IAssetParser;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
public class ZipAssetPackage implements IAssetPackage
{
@ -90,4 +94,23 @@ public class ZipAssetPackage implements IAssetPackage
}
catch(Exception e) { return null; }
}
@Override
public void getAllAssets(AssetLocation folder, Predicate<String> fileNames, int maxDepth, Collection<AssetLocation> result)
{
try(FileSystem system = FileSystems.newFileSystem(baseFolder, null))
{
Path start = system.getPath(folder.getActualLocation());
if(Files.notExists(start)) return;
try(Stream<Path> stream = Files.walk(start, maxDepth).filter(Files::isRegularFile).filter(T -> fileNames.test(T.getFileName().toString())))
{
for(Path path : IterableWrapper.wrap(stream.iterator()))
{
result.add(folder.subAsset(start.relativize(path).toString()));
}
}
catch(IOException e) { e.printStackTrace(); }
}
catch(Exception e) { e.printStackTrace(); }
}
}

View File

@ -6,7 +6,7 @@ import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet;
public class ResourceReloader
{
Set<IReloadableResource> resources = new ObjectLinkedOpenHashSet<IReloadableResource>();
Set<IReloadableResource> resources = new ObjectLinkedOpenHashSet<>();
boolean globalRemoval = false;
boolean reloading = false;

View File

@ -316,6 +316,16 @@ public class ColorObject
return "Color[r=" + getRedFloat() + ", g=" + getGreenFloat() + ", b=" + getBlueFloat() + ", a=" + getAlphaFloat() + "]";
}
public static byte[] toByteArray(int color, boolean alpha)
{
byte[] data = new byte[alpha ? 4 : 3];
data[0] = (byte)((color >> 16) & 0xFF);
data[1] = (byte)((color >> 8) & 0xFF);
data[2] = (byte)(color & 0xFF);
if(alpha) data[3] = (byte)((color >> 24) & 0xFF);
return data;
}
public static void pack(int color, boolean alpha, ByteBuffer buffer)
{
buffer.put((byte)((color >> 16) & 0xFF)).put((byte)((color >> 8) & 0xFF)).put((byte)(color & 0xFF));

View File

@ -1,118 +0,0 @@
package speiger.src.coreengine.rendering.gui;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;
import speiger.src.collections.chars.maps.impl.hash.Char2ObjectOpenHashMap;
import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.rendering.gui.renderer.FontRenderer;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.SimpleTexture;
import speiger.src.coreengine.rendering.textures.TextureManager;
public class FontLoader
{
public static FontRenderer createFont(AssetLocation location, String name)
{
return createFont(location, name, 0.35F);
}
public static FontRenderer createFont(AssetLocation location, String name, float scale)
{
try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location.subAsset(name+".fnt")))
{
Char2ObjectMap<CharInstance>[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap<CharInstance>(), new Char2ObjectOpenHashMap<CharInstance>()};
BufferedReader reader = asset.getStringReader();
FontInfo info = new FontInfo(convert(reader.readLine().split(", ")));
String value = null;
while((value = getNextValidLine(reader)) != null)
{
Map<String, Integer> dataMap = convert(value.split(", "));
CharInstance instance = createChar(dataMap, info);
instance.scale(scale);
maps[instance.isBold() ? 1 : 0].putIfAbsent(instance.getCharacter(), instance);
}
info.scale(scale);
return new FontRenderer(maps, new SimpleTexture(location.subAsset(name+"-Texture.png")), info.fontHeight, info.lineHeight);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
static String getNextValidLine(BufferedReader reader) throws Exception
{
String line = reader.readLine();
if(line == null)
{
return null;
}
else if(line.isEmpty() || line.startsWith("//"))
{
return getNextValidLine(reader);
}
return line;
}
static CharInstance createChar(Map<String, Integer> data, FontInfo info)
{
char character = (char)data.get("letter").intValue();
int minX = data.get("minX");
int minY = data.get("minY");
int maxX = data.get("maxX");
int maxY = data.get("maxY");
return new CharInstance(character, maxX - minX, maxY - minY, info.getTextureU(minX), info.getTextureV(minY), info.getTextureU(maxX), info.getTextureV(maxY), maxX - minX, data.getOrDefault("bold", 0).intValue() == 1);
}
static Map<String, Integer> convert(String[] data)
{
Map<String, Integer> map = new HashMap<String, Integer>();
for(String s : data)
{
int index = s.indexOf("=");
if(index != -1)
{
String[] split = s.split("=");
map.put(split[0], Integer.parseInt(split[1]));
}
}
return map;
}
public static class FontInfo
{
int height;
int width;
float fontHeight;
float lineHeight;
public FontInfo(Map<String, Integer> data)
{
width = data.get("textureWidth");
height = data.get("textureHeight");
fontHeight = data.get("fontHeight");
lineHeight = data.get("base");
}
public void scale(float scale)
{
fontHeight *= scale;
lineHeight *= scale;
}
public float getTextureU(float value)
{
return value / width;
}
public float getTextureV(float value)
{
return value / height;
}
}
}

View File

@ -8,6 +8,7 @@ import speiger.src.coreengine.rendering.gui.base.DebugOverlay;
import speiger.src.coreengine.rendering.gui.renderer.FontRenderer;
import speiger.src.coreengine.rendering.gui.renderer.GuiShader;
import speiger.src.coreengine.rendering.gui.renderer.UIRenderer;
import speiger.src.coreengine.rendering.gui.renderer.provider.FontManager;
import speiger.src.coreengine.rendering.input.events.KeyEvent.CharTypeEvent;
import speiger.src.coreengine.rendering.input.events.KeyEvent.KeyPressEvent;
import speiger.src.coreengine.rendering.input.events.MouseEvent;
@ -31,12 +32,13 @@ public abstract class GuiManager implements IWindowListener
protected ScaledResolution res;
protected long globalClock = 0L;
protected boolean isReloading = false;
protected FontRenderer font = FontLoader.createFont(AssetLocation.of("font"), "Roboto-Font");
protected FontRenderer font;
protected GuiShader shader = ShaderTracker.INSTANCE.register(GuiShader::create, T -> shader = T);
public GuiManager(Window window, EventBus bus)
public GuiManager(Window window, EventBus bus, FontManager manager)
{
this.window = window;
font = manager.loadFont(AssetLocation.of("font/roboto.json"), 18.5F);
bus.register(MouseEvent.class, this::onMouseEvent);
bus.register(KeyPressEvent.class, (T) -> T.setCanceled(onKeyPressed(T.key)));
bus.register(CharTypeEvent.class, (T) -> T.setCanceled(onCharTyped(T.character, T.codePoint)));

View File

@ -1,19 +1,24 @@
package speiger.src.coreengine.rendering.gui;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.SimpleTexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public class UITextures
{
public static final AssetLocation TEXTURE_LOCATION = AssetLocation.of("textures");
// public static final ITexture OK_SYMBOL = new SimpleTexture(sub("okSymbol.png"));
// public static final ITexture CANCLE_SYMBOL = new SimpleTexture(sub("cancelSymbol.png"));
public static final ITexture COLOR_WHEEL = new SimpleTexture(sub("colorWheel.png"));
public static final ITexture COLOR_WHEEL = ITexture.simple(sub("colorWheel.png")).makeReloadable();
public static AssetLocation sub(String name)
{
public static ITexture createReloadable(String name) {
return ITexture.simple(sub(name)).makeReloadable();
}
public static ITexture create(String name) {
return ITexture.simple(sub(name));
}
public static AssetLocation sub(String name) {
return TEXTURE_LOCATION.subAsset(name);
}
}

View File

@ -2,7 +2,7 @@ package speiger.src.coreengine.rendering.gui.components;
import speiger.src.coreengine.math.misc.ColorObject;
import speiger.src.coreengine.rendering.gui.GuiComponent;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public class IconComponent extends GuiComponent
{

View File

@ -52,6 +52,7 @@ public class ListComponent<T extends IListEntry> extends GuiComponent
protected int selectionMode = 1;
protected int updateMode = 1;
protected float entryHeight;
protected float cachedWidth = 0F;
protected ScrollBarComponent verticalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY);
protected ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorObject.LIGHT_GRAY).setHorizontal(true);
protected Vec2i lastMouse = Vec2i.newMutable();
@ -416,6 +417,11 @@ public class ListComponent<T extends IListEntry> extends GuiComponent
return rangeIterator(start, MathUtils.clamp(0, entries.size() - 1, start + getIndexWidth()));
}
public float getCachedWidth()
{
return cachedWidth;
}
@Override
protected void repaint()
{
@ -434,6 +440,7 @@ public class ListComponent<T extends IListEntry> extends GuiComponent
{
width = Math.max(width, entries.get(i).getWidth());
}
this.cachedWidth = width;
boolean lastVertical = this.verticalBar.isInUse();
boolean lastHorizontal = this.horizontalBar.isInUse();
verticalBar.setScrollMax(MathUtils.ceil(this.entries.size() * entryHeight));

View File

@ -2,7 +2,7 @@ package speiger.src.coreengine.rendering.gui.components.icon;
import speiger.src.coreengine.math.misc.ColorObject;
import speiger.src.coreengine.rendering.gui.renderer.UIRenderer;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public class TexturedIcon implements IIcon
{

View File

@ -0,0 +1,241 @@
package speiger.src.coreengine.rendering.gui.helper;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas.AtlasEntry;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas.Builder;
public class FontBuilder
{
public static final int LITERAL = 1;
public static final int PLAIN = 2;
public static final int BOLD = 4;
public static ObjectObjectPair<BufferedImage, JsonObject> createBitmapFont(InputStream stream, float size)
{
ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> result = createBitmapFont(stream, "ISO-8859-1", PLAIN | BOLD, size);
if(result == null) return null;
JsonArray array = new JsonArray();
result.getValue().forEach(T -> array.add(T.seralize()));
JsonObject info = new JsonObject();
ObjectObjectPair<Vec2i, BufferedImage> key = result.getKey();
info.addProperty("width", key.getValue().getWidth());
info.addProperty("height", key.getValue().getHeight());
info.addProperty("base", key.getKey().getX());
info.addProperty("charHeight", key.getKey().getY());
info.addProperty("tabs", 4);
JsonObject data = new JsonObject();
data.addProperty("type", "bitmap");
data.addProperty("file", "?");
data.add("info", info);
data.add("chars", array);
return ObjectObjectPair.of(key.getValue(), data);
}
public static ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> createBitmapFont(InputStream ttf, String characters, int flags, float size)
{
try
{
Map<AssetLocation, CharData> toDraw = new Object2ObjectLinkedOpenHashMap<>();
Builder builder = TextureAtlas.create();
Consumer<CharData> data = T -> {
AssetLocation location = T.asLocation();
toDraw.put(location, T);
if(T.width > 0 && !builder.add(location, T.width, T.getExtraY(T.height))) throw new IllegalStateException("Character: " + location + " isnt Accepted, W=" + T.width + ", H=" + T.height);
};
Font font = Font.createFont(Font.TRUETYPE_FONT, ttf).deriveFont(size);
String validChars = (flags & LITERAL) != 0 ? characters : getChars(characters, font);
if((flags & PLAIN) != 0) loadFontData(font, validChars, data);
if((flags & BOLD) != 0) loadFontData(font.deriveFont(Font.BOLD), validChars, data);
ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> result = ObjectObjectPair.mutableValue(new ObjectArrayList<>());
builder.buildHollow((K, V) -> {
BufferedImage image = new BufferedImage(K.getX(), K.getY(), BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
graphics.setFont(font);
FontMetrics metric = graphics.getFontMetrics();
int extra = 0;
for(AtlasEntry entry : V)
{
CharData pair = toDraw.remove(entry.getLocation());
extra = pair.extraY;
graphics.setFont(pair.getFont());
graphics.setColor(Color.WHITE);
graphics.drawString(Character.toString(pair.getLetter()), entry.getX()-pair.xOffset, pair.getExtraY(entry.getY()+metric.getAscent()));
result.getValue().add(new WrittenChar(pair.getLetter(), entry.getX(), entry.getY(), entry.getWidth(), entry.getHeight(), pair.isBold()));
}
toDraw.values().forEach(T -> result.getValue().add(new WrittenChar(T.getLetter(), 0, 0, 0, 0, T.isBold())));
result.setKey(ObjectObjectPair.of(Vec2i.newVec(metric.getAscent()+extra, metric.getHeight()+extra), image));
graphics.dispose();
});
return result;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
private static float convert(float pos, float width)
{
return pos / width;
}
private static String getChars(String s, Font font)
{
CharsetEncoder encoder = Charset.forName(s).newEncoder();
StringBuilder builder = new StringBuilder();
for(char c = 0;c < Character.MAX_VALUE;c++)
{
if(encoder.canEncode(c) && font.canDisplay(c)) builder.append(c);
}
return builder.toString();
}
private static void loadFontData(Font font, String chars, Consumer<CharData> listener)
{
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = (Graphics2D)image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
graphics.setFont(font);
FontMetrics metric = graphics.getFontMetrics();
int extraHeight = 0;
List<CharData> data = new ObjectArrayList<>();
for(char letter : chars.toCharArray())
{
Rectangle rect = font.layoutGlyphVector(graphics.getFontRenderContext(), new char[] {letter }, 0, 1, 0).getGlyphPixelBounds(0, graphics.getFontRenderContext(), 0.0F, 0.0F);
extraHeight = Math.min(extraHeight, rect.y);
data.add(new CharData(font, letter, rect, metric.charWidth(letter), metric.getHeight(), font.isBold()));
}
extraHeight = -(extraHeight+metric.getAscent());
for(int i = 0,m=data.size();i<m;listener.accept(data.get(i++).offset(extraHeight)));
graphics.dispose();
}
public static class WrittenChar
{
char letter;
int x;
int y;
int width;
int height;
boolean bold;
public WrittenChar(char letter, int x, int y, int width, int height, boolean bold)
{
this.letter = letter;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.bold = bold;
}
public JsonObject seralize()
{
JsonObject obj = new JsonObject();
obj.addProperty("char", (int)letter);
obj.addProperty("minX", x);
obj.addProperty("minY", y);
obj.addProperty("maxX", x+width);
obj.addProperty("maxY", y+height);
obj.addProperty("bold", bold);
return obj;
}
public CharInstance create(int textureWidth, int textureHeight)
{
return new CharInstance(letter, width, height, convert(x, textureWidth), convert(y, textureHeight), convert(x+width, textureWidth), convert(y+height, textureHeight), width, bold);
}
}
private static class CharData
{
Font font;
char letter;
int xOffset;
int width;
int height;
boolean bold;
int extraY = 0;
public CharData(Font font, char letter, Rectangle bounds, int width, int height, boolean bold)
{
this.font = font;
this.letter = letter;
if(bounds.x >= 0) this.width = width;
else
{
this.width = bounds.width == 0 ? width : bounds.width;
xOffset = bounds.x;
}
this.height = height;
this.bold = bold;
}
private CharData offset(int offset)
{
extraY += offset;
return this;
}
public boolean isBold()
{
return bold;
}
public Font getFont()
{
return font;
}
public int getExtraY(int asent)
{
return extraY + asent;
}
public char getLetter()
{
return letter;
}
public AssetLocation asLocation()
{
return AssetLocation.of("base", font.getFontName().replaceAll(" ", "_") + (bold ? "_Bold" : "") + "_" + ((int)letter));
}
}
}

View File

@ -7,6 +7,7 @@ public class TextFilter
public static final Predicate<String> INTEGER_ONLY = T -> {
try {
if(T == null || T.isEmpty());
else if(T.length() == 1 && T.charAt(0) == '-');
else Integer.parseInt(T);
return true;
}
@ -16,6 +17,7 @@ public class TextFilter
public static final Predicate<String> FLOAT_ONLY = T -> {
try {
if(T == null || T.isEmpty());
else if(T.length() == 1 && T.charAt(0) == '-');
else Float.parseFloat(T);
return true;
}

View File

@ -5,7 +5,6 @@ import java.util.Locale;
import org.lwjgl.opengl.GL11;
import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap;
import speiger.src.collections.floats.lists.FloatArrayList;
import speiger.src.collections.floats.lists.FloatList;
import speiger.src.collections.objects.lists.ObjectArrayList;
@ -20,11 +19,12 @@ import speiger.src.coreengine.rendering.gui.renderer.lexer.Line;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextContext;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextContext.WordContext;
import speiger.src.coreengine.rendering.gui.renderer.lexer.TextLexer;
import speiger.src.coreengine.rendering.gui.renderer.provider.IFontProvider;
import speiger.src.coreengine.rendering.models.DrawCall;
import speiger.src.coreengine.rendering.tesselation.IVertexBuilder;
import speiger.src.coreengine.rendering.tesselation.Tesselator;
import speiger.src.coreengine.rendering.tesselation.VertexType;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.utils.helpers.TextUtil;
public class FontRenderer implements IFontRenderer
@ -36,45 +36,51 @@ public class FontRenderer implements IFontRenderer
public static final char LINE_SEPERATOR = '\n';
Tesselator bufferBuilder = new Tesselator(655340);
IFontProvider provider;
final TextLexer lexer = new TextLexer(this);
final DelayedRenderBuffer lineBuffer = new DelayedRenderBuffer();
final Char2ObjectMap<CharInstance>[] chars;
final ITexture texture;
final float height;
final float baseLine;
final float space;
public FontRenderer(Char2ObjectMap<CharInstance>[] chars, ITexture texture, float height, float baseLine)
public void setProvider(IFontProvider provider)
{
this.chars = chars;
this.texture = texture;
this.height = height;
this.baseLine = baseLine;
space = chars[0].get(' ').getXAdvance();
this.provider = provider;
}
@Override
public CharInstance getInstance(char letter, boolean isBold)
{
return chars[isBold ? 1 : 0].get(letter);
return provider.getCharacter(letter, isBold);
}
@Override
public float getFontHeight()
{
return height;
return provider.getFontHeight();
}
@Override
public float getBaseLine()
{
return baseLine;
return provider.getBaseLine();
}
@Override
public ITexture getTexture()
{
return texture;
return provider.getTexture();
}
public IFontProvider getProvider()
{
return provider;
}
public void destory()
{
if(provider != null)
{
provider.destroy();
provider = null;
}
}
public List<DrawCall> renderText(String text, float x, float y, float z)
@ -96,12 +102,12 @@ public class FontRenderer implements IFontRenderer
{
xOffset += renderChar(letter, xOffset, yOffset, context.getScale(), effects.italic, effects.flipped, textColor, builder, true);
}
yOffset += height * context.getScale();
yOffset += getFontHeight() * context.getScale();
}
bufferBuilder.finishData();
if(bufferBuilder.getVertexCount() > 0)
{
drawCalls.add(bufferBuilder.getDrawCall(texture.getTextureID()));
drawCalls.add(bufferBuilder.getDrawCall(getTexture().getTextureID()));
}
bufferBuilder.setOffset(0F, 0F, 0F);
return drawCalls;
@ -125,8 +131,8 @@ public class FontRenderer implements IFontRenderer
return;
}
bufferBuilder.begin(GL11.GL_TRIANGLES, VertexType.UI);
int maxLanes = component.isHeightLimited() ? Math.min((int)(boxHeight / (height * context.getScale())), lines.size()) : lines.size();
float maxHeight = maxLanes * height * context.getScale();
int maxLanes = component.isHeightLimited() ? Math.min((int)(boxHeight / (getFontHeight() * context.getScale())), lines.size()) : lines.size();
float maxHeight = maxLanes * getFontHeight() * context.getScale();
float maxWidth = 0F;
float yOffset = component.getVertical().align(boxHeight, maxHeight);
float startX = component.getHorizontal().align(boxWidth, lines.get(0).getWidth());
@ -182,11 +188,11 @@ public class FontRenderer implements IFontRenderer
{
addUnderline(underline, xOffset - underline, yOffset, textColor, lineBuffer, false);
}
yOffset += height * context.getScale();
yOffset += getFontHeight() * context.getScale();
component.getMetadata().addLine(lines.get(i));
}
maxWidth /= 2;
buffer.finishShape(texture.getTextureID(), bufferBuilder);
buffer.finishShape(getTexture().getTextureID(), bufferBuilder);
if(lineBuffer.hasData())
{
Tesselator tes = buffer.start(GL11.GL_TRIANGLES, VertexType.UI).offset(0F, 0F, 0.001F);
@ -201,9 +207,9 @@ public class FontRenderer implements IFontRenderer
switch(instance.getCharacter())
{
case TAB:
return space * 4 * scale;
return provider.getTabWidth() * scale;
case SPACE:
return space * scale;
return provider.getSpaceWidth() * scale;
}
if(instance.getXAdvance() <= 0F)
{
@ -231,7 +237,7 @@ public class FontRenderer implements IFontRenderer
{
float lineWidth = lines.get(i).getWidth();
float xOffset = align.align(width, lineWidth);
float maxY = flipPos ? yPos - height : yPos + height;
float maxY = flipPos ? yPos - getFontHeight() : yPos + getFontHeight();
tes.pos(xOffset, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
tes.pos(xOffset, yPos, 0.0F).tex(0F, 0F).color4f(color).endVertex();
tes.pos(xOffset + lineWidth, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
@ -244,12 +250,12 @@ public class FontRenderer implements IFontRenderer
protected void addUnderline(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer, boolean flipPos)
{
float minY = yStart + baseLine + 0.5F;
float maxY = yStart + baseLine + 1.5F;
float minY = yStart + getBaseLine() + 0.5F;
float maxY = yStart + getBaseLine() + 1.5F;
if(flipPos)
{
minY = yStart - baseLine - 0.5F;
maxY = yStart - baseLine - 1.5F;
minY = yStart - getBaseLine() - 0.5F;
maxY = yStart - getBaseLine() - 1.5F;
}
buffer.pos(xStart, maxY, 0F).tex(0F, 0F).color4f(color).endVertex();
buffer.pos(xStart, minY, 0F).tex(0F, 0F).color4f(color).endVertex();
@ -261,8 +267,8 @@ public class FontRenderer implements IFontRenderer
protected void addStrikeThrough(float xStart, float width, float yStart, ColorObject color, IVertexBuilder buffer)
{
float minY = yStart + height / 2.0F;
float maxY = yStart + height / 2.0F + 1.4F;
float minY = yStart + getFontHeight() / 2.0F;
float maxY = yStart + getFontHeight() / 2.0F + 1.4F;
buffer.pos(xStart, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
buffer.pos(xStart, minY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
buffer.pos(xStart + width, maxY, 0.0F).tex(0F, 0F).color4f(color).endVertex();
@ -301,9 +307,9 @@ public class FontRenderer implements IFontRenderer
switch(letter)
{
case SPACE:
return space;
return provider.getSpaceWidth();
case TAB:
return space * 4;
return provider.getTabWidth();
default:
CharInstance instance = getInstance(letter, bold);
return instance == null ? 0F : instance.getXAdvance();
@ -321,7 +327,7 @@ public class FontRenderer implements IFontRenderer
char character = text.charAt(i);
if(LINE_SEPERATOR == character)
{
result = Math.max(result, current += space);
result = Math.max(result, current += provider.getSpaceWidth());
current = 0.0F;
continue;
}
@ -391,13 +397,13 @@ public class FontRenderer implements IFontRenderer
@Override
public float getTextHeight(String text, int flags)
{
return getTextLengths(text, flags).length - 1 * height;
return getTextLengths(text, flags).length - 1 * getFontHeight();
}
@Override
public boolean isCharValid(char letter)
{
return chars[0].containsKey(letter);
return provider.isCharacterValid(letter);
}
@Override

View File

@ -1,7 +1,7 @@
package speiger.src.coreengine.rendering.gui.renderer;
import speiger.src.coreengine.rendering.gui.components.TextComponent;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.base.ITexture;
public interface IFontRenderer
{
@ -62,6 +62,12 @@ public interface IFontRenderer
float xAdvance;
boolean bold;
public CharInstance(char character, boolean bold)
{
this.character = character;
this.bold = bold;
}
public CharInstance(char character, int width, int height, float minU, float minV, float maxU, float maxV, int xAdvance, boolean bold)
{
this.character = character;

View File

@ -28,8 +28,8 @@ import speiger.src.coreengine.rendering.shader.uniforms.UniformVec2f;
import speiger.src.coreengine.rendering.tesselation.GLCall;
import speiger.src.coreengine.rendering.tesselation.Tesselator;
import speiger.src.coreengine.rendering.tesselation.VertexType;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.GLUtils;
import speiger.src.coreengine.utils.collections.pools.SimplePool;

View File

@ -0,0 +1,141 @@
package speiger.src.coreengine.rendering.gui.renderer.provider;
import java.awt.image.BufferedImage;
import java.util.List;
import com.google.gson.JsonObject;
import speiger.src.collections.chars.maps.impl.hash.Char2ObjectOpenHashMap;
import speiger.src.collections.chars.maps.interfaces.Char2ObjectMap;
import speiger.src.collections.chars.utils.maps.Char2ObjectMaps;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.gui.helper.FontBuilder;
import speiger.src.coreengine.rendering.gui.helper.FontBuilder.WrittenChar;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.utils.helpers.JsonUtil;
public class BitmapFontProvider implements IFontProvider
{
FontInfo info;
ITexture texture;
Char2ObjectMap<CharInstance>[] instances;
float space;
public BitmapFontProvider(FontInfo info, ITexture texture, Char2ObjectMap<CharInstance>[] instances)
{
this.info = info;
this.texture = texture;
this.instances = instances;
if(instances[0].containsKey(' ')) space = instances[0].get(' ').getXAdvance();
else if(instances[1].containsKey(' ')) space = instances[1].get(' ').getXAdvance();
}
@Override
public void destroy()
{
if(texture != null)
{
texture.destroy();
texture = null;
}
}
@Override
public ITexture getTexture()
{
return texture;
}
@Override
public boolean isCharacterValid(char value)
{
return instances[0].containsKey(value) || instances[1].containsKey(value);
}
@Override
public CharInstance getCharacter(char value, boolean bold)
{
Char2ObjectMap<CharInstance> map = instances[bold ? 1 : 0];
return (map.isEmpty() ? instances[bold ? 0 : 1] : map).get(value);
}
@Override
public float getFontHeight()
{
return info.fontHeight;
}
@Override
public float getBaseLine()
{
return info.fontBase;
}
@Override
public float getSpaceWidth()
{
return space;
}
@Override
public float getTabWidth()
{
return space * info.tabs;
}
public static IFontProvider load(JsonObject object, float desiredSize, AssetManager manager)
{
FontInfo info = new FontInfo(object.getAsJsonObject("info"));
float multiplier = info.setDesiredHeight(desiredSize);
Char2ObjectMap<CharInstance>[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap<CharInstance>(), new Char2ObjectOpenHashMap<CharInstance>()};
JsonUtil.iterate(object.get("chars"), T -> {
CharInstance instance = info.create(T);
instance.scale(multiplier);
maps[instance.isBold() ? 1 : 0].put(instance.getCharacter(), instance);
});
if(maps[0].isEmpty()) maps[0] = Char2ObjectMaps.empty();
if(maps[1].isEmpty()) maps[1] = Char2ObjectMaps.empty();
return new BitmapFontProvider(info, ITexture.simple(AssetLocation.of(object.get("file").getAsString())), maps);
}
public static IFontProvider create(JsonObject object, float desiredSize, AssetManager manager)
{
try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(AssetLocation.of(object.get("file").getAsString())))
{
JsonObject info = object.getAsJsonObject("info");
int tabs = JsonUtil.getOrDefault(info, "tabs", 4);
boolean literal = JsonUtil.getOrDefault(info, "literal", false);
boolean plain = JsonUtil.getOrDefault(info, "plain", true);
boolean bold = JsonUtil.getOrDefault(info, "bold", true);
if(!plain && !bold) throw new IllegalStateException("You need a plain or bold font at the very least");
int flags = (literal ? FontBuilder.LITERAL : 0) | (plain ? FontBuilder.PLAIN : 0) | (bold ? FontBuilder.BOLD : 0);
ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> written = FontBuilder.createBitmapFont(asset.getStream(), info.get("charset").getAsString(), flags, info.get("size").getAsFloat());
BufferedImage image = written.getKey().getValue();
Vec2i size = written.getKey().getKey();
FontInfo fontInfo = new FontInfo(image.getWidth(), image.getHeight(), size.getY(), size.getX(), tabs);
float mulitplier = fontInfo.setDesiredHeight(desiredSize);
Char2ObjectMap<CharInstance>[] maps = new Char2ObjectMap[]{new Char2ObjectOpenHashMap<CharInstance>(), new Char2ObjectOpenHashMap<CharInstance>()};
for(WrittenChar entry : written.getValue())
{
CharInstance instance = entry.create(fontInfo.textureWidth, fontInfo.textureHeight);
instance.scale(mulitplier);
maps[instance.isBold() ? 1 : 0].put(instance.getCharacter(), instance);
}
if(maps[0].isEmpty()) maps[0] = Char2ObjectMaps.empty();
if(maps[1].isEmpty()) maps[1] = Char2ObjectMaps.empty();
return new BitmapFontProvider(fontInfo, ITexture.direct(image), maps);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,100 @@
package speiger.src.coreengine.rendering.gui.renderer.provider;
import java.util.List;
import java.util.Map;
import com.google.gson.JsonObject;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2FloatLinkedOpenHashMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2FloatMap;
import speiger.src.collections.objects.maps.interfaces.Object2FloatMap.Entry;
import speiger.src.collections.objects.utils.maps.Object2FloatMaps;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.rendering.gui.renderer.FontRenderer;
public class FontManager implements IReloadableResource
{
Map<AssetLocation, FontRenderer> fontRenders = new Object2ObjectLinkedOpenHashMap<>();
Object2FloatMap<AssetLocation> fontSizes = new Object2FloatLinkedOpenHashMap<>();
Map<String, IFontLoader> loaders = new Object2ObjectOpenHashMap<>();
AssetManager manager;
public FontManager()
{
registerFontLoader("bitmap", BitmapFontProvider::load);
registerFontLoader("java-ttf", BitmapFontProvider::create);
}
public void setAssetManager(AssetManager manager)
{
this.manager = manager;
}
public void registerFontLoader(String id, IFontLoader provider)
{
loaders.put(id, provider);
}
public FontRenderer loadFont(AssetLocation location, float desiredSize)
{
FontRenderer render = fontRenders.get(location);
if(render == null)
{
IFontProvider provider = loadProvider(location, desiredSize);
if(provider != null)
{
render = new FontRenderer();
render.setProvider(provider);
fontRenders.put(location, render);
fontSizes.putIfAbsent(location, desiredSize);
}
}
return render;
}
private IFontProvider loadProvider(AssetLocation location, float desiredSize)
{
try(IAsset asset = manager.getAsset(location))
{
JsonObject obj = asset.getJsonObject();
IFontLoader loader = loaders.get(obj.get("type").getAsString());
if(loader == null) return null;
return loader.create(obj, desiredSize, manager);
}
catch(Exception e) { e.printStackTrace(); }
return null;
}
public static interface IFontLoader
{
public IFontProvider create(JsonObject obj, float desiredSize, AssetManager loader);
}
@Override
public void reload()
{
List<IFontProvider> providers = new ObjectArrayList<>();
for(Entry<AssetLocation> entry : Object2FloatMaps.fastIterable(fontSizes))
{
AssetLocation location = entry.getKey();
FontRenderer font = fontRenders.get(location);
providers.add(font.getProvider());
font.setProvider(loadProvider(location, entry.getFloatValue()));
}
for(int i = 0,m=providers.size();i<m;providers.get(i++).destroy());
}
@Override
public void destroy()
{
fontRenders.values().forEach(FontRenderer::destory);
fontRenders.clear();
}
}

View File

@ -0,0 +1,72 @@
package speiger.src.coreengine.rendering.gui.renderer.provider;
import com.google.gson.JsonObject;
import speiger.src.coreengine.rendering.gui.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.utils.helpers.JsonUtil;
public interface IFontProvider
{
public ITexture getTexture();
public boolean isCharacterValid(char value);
public CharInstance getCharacter(char value, boolean bold);
public float getFontHeight();
public float getBaseLine();
public float getSpaceWidth();
public float getTabWidth();
public void destroy();
public static class FontInfo
{
public int textureWidth;
public int textureHeight;
public float fontHeight;
public float fontBase;
public int tabs;
public FontInfo(JsonObject obj)
{
this(obj.get("width").getAsInt(), obj.get("height").getAsInt(), obj.get("charHeight").getAsInt(), obj.get("base").getAsInt(), JsonUtil.getOrDefault(obj, "tabs", 4));
}
public FontInfo(int textureWidth, int textureHeight, float fontHeight, float fontBase, int tabs)
{
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
this.fontHeight = fontHeight;
this.fontBase = fontBase;
this.tabs = tabs;
}
public float setDesiredHeight(float desired)
{
float multiplier = desired / fontHeight;
fontHeight *= multiplier;
fontBase *= multiplier;
return multiplier;
}
public CharInstance create(JsonObject obj)
{
return create((char)obj.get("char").getAsInt(), obj.get("minX").getAsInt(), obj.get("minY").getAsInt(), obj.get("maxX").getAsInt(), obj.get("maxY").getAsInt(), JsonUtil.getOrDefault(obj, "bold", false));
}
public CharInstance create(char character, int minX, int minY, int maxX, int maxY, boolean bold)
{
return new CharInstance(character, maxX - minX, maxY - minY, getTextureU(minX), getTextureV(minY), getTextureU(maxX), getTextureV(maxY), maxX - minX, bold);
}
public float getTextureU(float value)
{
return value / textureWidth;
}
public float getTextureV(float value)
{
return value / textureHeight;
}
}
}

View File

@ -0,0 +1,53 @@
package speiger.src.coreengine.rendering.models;
import java.util.Map;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
public enum DataType
{
BYTE("byte", 1),
SHORT("short", 2),
INT("int", 4),
LONG("long", 8),
FLOAT("float", 4),
DOUBLE("double", 8);
static final Map<String, DataType> BY_ID = new Object2ObjectOpenHashMap<>();
final String type;
final int byteSize;
private DataType(String type, int byteSize)
{
this.type = type;
this.byteSize = byteSize;
}
public String getType()
{
return type;
}
public int getByteSize()
{
return byteSize;
}
public boolean isFloatingPoint()
{
return this == FLOAT || this == DOUBLE;
}
public static DataType byID(String id)
{
return BY_ID.get(id);
}
static
{
for(DataType type : values())
{
BY_ID.put(type.getType(), type);
}
}
}

View File

@ -1,42 +1,56 @@
package speiger.src.coreengine.rendering.models;
import java.util.Map;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL30;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
public class GLDataType
{
static final Int2ObjectMap<GLDataType> ID_TO_TYPE = new Int2ObjectOpenHashMap<>();
static final Map<String, GLDataType> NAME_TO_TYPE = new Object2ObjectOpenHashMap<>();
//Normal Types
public static final GLDataType BYTE = new GLDataType(GL11.GL_BYTE, 1);
public static final GLDataType UNSIGNED_BYTE = new GLDataType(GL11.GL_UNSIGNED_BYTE, 1);
public static final GLDataType SHORT = new GLDataType(GL11.GL_SHORT, 2);
public static final GLDataType UNSIGNED_SHORT = new GLDataType(GL11.GL_UNSIGNED_SHORT, 2);
public static final GLDataType INT = new GLDataType(GL11.GL_INT, 4);
public static final GLDataType UNSIGNED_INT = new GLDataType(GL11.GL_UNSIGNED_INT, 4);
public static final GLDataType FLOAT = new GLDataType(GL11.GL_FLOAT, 4);
public static final GLDataType BYTE = new GLDataType(GL11.GL_BYTE, DataType.BYTE, "byte");
public static final GLDataType UNSIGNED_BYTE = new GLDataType(GL11.GL_UNSIGNED_BYTE, DataType.BYTE, "u_byte");
public static final GLDataType SHORT = new GLDataType(GL11.GL_SHORT, DataType.SHORT, "short");
public static final GLDataType UNSIGNED_SHORT = new GLDataType(GL11.GL_UNSIGNED_SHORT, DataType.SHORT, "u_short");
public static final GLDataType INT = new GLDataType(GL11.GL_INT, DataType.INT, "int");
public static final GLDataType UNSIGNED_INT = new GLDataType(GL11.GL_UNSIGNED_INT, DataType.INT, "u_int");
public static final GLDataType FLOAT = new GLDataType(GL11.GL_FLOAT, DataType.FLOAT, "float");
public static final GLDataType DOUBLE = new GLDataType(GL11.GL_DOUBLE, DataType.DOUBLE, "double");
//Compression Types
public static final GLDataType UNSIGNED_INT_10_10_10_2 = new GLDataType(GL12.GL_UNSIGNED_INT_10_10_10_2, 4, true);
public static final GLDataType UNSIGNED_INT_2_10_10_10_REV = new GLDataType(GL12.GL_UNSIGNED_INT_2_10_10_10_REV, 4, true);
public static final GLDataType UNSIGNED_INT_10_10_10_2 = new GLDataType(GL12.GL_UNSIGNED_INT_10_10_10_2, DataType.INT, true, "u_int_10_10_10_2");
public static final GLDataType UNSIGNED_INT_2_10_10_10_REV = new GLDataType(GL12.GL_UNSIGNED_INT_2_10_10_10_REV, DataType.INT, true, "u_int_2_10_10_10_rev");
//Special Types
public static final GLDataType UNSIGNED_INT_10F_11F_11F_REV = new GLDataType(GL30.GL_UNSIGNED_INT_10F_11F_11F_REV, 4, true);
public static final GLDataType UNSIGNED_INT_5_9_9_9_REV = new GLDataType(GL30.GL_UNSIGNED_INT_5_9_9_9_REV, 4, true);
public static final GLDataType UNSIGNED_INT_10F_11F_11F_REV = new GLDataType(GL30.GL_UNSIGNED_INT_10F_11F_11F_REV, DataType.INT, true, "u_int_10_11_11_rev");
public static final GLDataType UNSIGNED_INT_5_9_9_9_REV = new GLDataType(GL30.GL_UNSIGNED_INT_5_9_9_9_REV, DataType.INT, true, "u_int_5_9_9_9_rev");
final int glType;
final int byteSize;
final DataType dataType;
final boolean ignoreAttributeSize;
final String name;
public GLDataType(int glType, int byteSize)
public GLDataType(int glType, DataType dataType, String name)
{
this(glType, byteSize, false);
this(glType, dataType, false, name);
}
public GLDataType(int glType, int byteSize, boolean ignoreAttributeSize)
public GLDataType(int glType, DataType dataType, boolean ignoreAttributeSize, String name)
{
this.glType = glType;
this.byteSize = byteSize;
this.dataType = dataType;
this.ignoreAttributeSize = ignoreAttributeSize;
this.name = name;
ID_TO_TYPE.put(glType, this);
NAME_TO_TYPE.put(name, this);
}
public int getGLType()
@ -44,13 +58,28 @@ public class GLDataType
return glType;
}
public String getName()
{
return name;
}
public int getByteSize()
{
return byteSize;
return dataType.getByteSize();
}
public boolean isSpecialType()
{
return ignoreAttributeSize;
}
public DataType getDataType()
{
return dataType;
}
public int calulateSize(int attributeSize)
{
return ignoreAttributeSize ? byteSize : attributeSize * byteSize;
return ignoreAttributeSize ? dataType.getByteSize() : attributeSize * dataType.getByteSize();
}
}

View File

@ -9,7 +9,7 @@ import java.util.function.Consumer;
import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryUtil;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
import speiger.src.coreengine.rendering.utils.AllocationTracker;
import speiger.src.coreengine.utils.io.GameLog;
@ -213,12 +213,12 @@ public class VertexBuffer
return this;
}
public VertexBuffer fillBuffer(List<Entry<byte[]>> data)
public VertexBuffer fillBuffer(List<IntObjectPair<byte[]>> data)
{
return fillBuffer(0, data);
}
public VertexBuffer fillBuffer(int byteOffset, List<Entry<byte[]>> data)
public VertexBuffer fillBuffer(int byteOffset, List<IntObjectPair<byte[]>> data)
{
if(data.isEmpty())
{
@ -228,7 +228,7 @@ public class VertexBuffer
ByteBuffer buffer = GL15.glMapBuffer(bufferType, GL15.GL_WRITE_ONLY);
for(int i = 0,m=data.size();i<m;i++)
{
Entry<byte[]> entry = data.get(i);
IntObjectPair<byte[]> entry = data.get(i);
buffer.position(entry.getIntKey()+byteOffset);
buffer.put(entry.getValue());
totalData += entry.getValue().length;

View File

@ -7,7 +7,7 @@ import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL32;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class TextureAttachment implements IFrameAttachment
{

View File

@ -1,7 +1,7 @@
package speiger.src.coreengine.rendering.shader.uniforms;
import speiger.src.coreengine.rendering.textures.ITexture;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class UniformTexture extends UniformInt
{

View File

@ -1,22 +0,0 @@
package speiger.src.coreengine.rendering.textures;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
public interface ITexture extends IReloadableResource
{
public int getTextureID();
public void bindTexture();
public void deleteTexture();
public int getWidth();
public int getHeight();
public float getUMin();
public float getVMin();
public float getUMax();
public float getVMax();
public default float getInterpolatedU(float u){return getUMin() + ((getUMax() - getUMin()) * u);}
public default float getINterpolatedV(float v){return getUMin() + ((getUMax() - getUMin()) * v);}
}

View File

@ -1,12 +1,14 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.base;
public abstract class AbstractTexture implements ITexture
{
int textureID;
protected int textureID;
public AbstractTexture()
@Override
public ITexture makeReloadable()
{
TextureManager.INSTANCE.addTexture(this);
return this;
}
public void setTextureID(int textureID)

View File

@ -0,0 +1,56 @@
package speiger.src.coreengine.rendering.textures.base;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.rendering.textures.normal.DirectTexture;
import speiger.src.coreengine.rendering.textures.normal.SimpleTexture;
import speiger.src.coreengine.rendering.textures.stb.STBDirectTexture;
import speiger.src.coreengine.rendering.textures.stb.STBTexture;
public interface ITexture extends IReloadableResource
{
public int getTextureID();
public void bindTexture();
public void deleteTexture();
public int getWidth();
public int getHeight();
public float getUMin();
public float getVMin();
public float getUMax();
public float getVMax();
public ITexture makeReloadable();
public default float getInterpolatedU(float u){return getUMin() + ((getUMax() - getUMin()) * u);}
public default float getInterpolatedV(float v){return getUMin() + ((getUMax() - getUMin()) * v);}
public static ITexture simple(AssetLocation location) {
return new STBTexture(location);
}
public static ITexture direct(ByteBuffer stbImageData, int width, int height) {
return new STBDirectTexture(stbImageData, width, height);
}
public static ITexture direct(long stbImageData, int width, int height) {
return new STBDirectTexture(stbImageData, width, height);
}
public static ITexture direct(BufferedImage imageData) {
return new STBDirectTexture(imageData);
}
public static ITexture awtSimple(AssetLocation location) {
return new SimpleTexture(location);
}
public static ITexture awtDirect(BufferedImage imageData) {
return new DirectTexture(imageData);
}
}

View File

@ -0,0 +1,27 @@
package speiger.src.coreengine.rendering.textures.base;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.assets.IAssetParser;
public class NativeMemoryParser implements IAssetParser<ByteBuffer>
{
@Override
public ByteBuffer parseAsset(Path path, Consumer<Closeable> autoCloser) throws IOException
{
byte[] data = Files.readAllBytes(path);
ByteBuffer buffer = MemoryUtil.memAlloc(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
}

View File

@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.base;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
@ -13,6 +13,8 @@ import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.assets.reloader.ResourceReloader;
import speiger.src.coreengine.rendering.textures.base.ITexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.GLUtils;
import speiger.src.coreengine.rendering.utils.states.GLState;
import speiger.src.coreengine.utils.io.GameLog;

View File

@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.base;
public class WrappedTexture implements ITexture
{
@ -8,7 +8,13 @@ public class WrappedTexture implements ITexture
{
this.textureId = textureId;
}
@Override
public ITexture makeReloadable()
{
return this;
}
@Override
public void reload()
{

View File

@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.custom;
import java.nio.ByteBuffer;
import java.util.Iterator;
@ -16,6 +16,8 @@ import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.ints.utils.maps.Int2ObjectMaps;
import speiger.src.collections.utils.ITrimmable;
import speiger.src.coreengine.math.BitUtil;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
import speiger.src.coreengine.rendering.utils.AllocationTracker;
public class DynamicTexture extends AbstractTexture
@ -132,8 +134,9 @@ public class DynamicTexture extends AbstractTexture
@Override
public void reload()
{
TextureManager.INSTANCE.removeTexture(getTextureID());
int old = getTextureID();
setTextureID(GL11.glGenTextures());
TextureManager.INSTANCE.removeTexture(old);
dirtyChunks.clear();
first = true;
updateData();

View File

@ -0,0 +1,304 @@
package speiger.src.coreengine.rendering.textures.custom;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.collections.objects.sets.ObjectOpenHashSet;
import speiger.src.collections.objects.utils.ObjectIterators;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.textures.custom.TextureAtlas;
/**
* Inspired by: <a href=https://github.com/lukaszdk/texture-atlas-generator/blob/master/AtlasGenerator.java>AtlasGenerator</a>
*/
public class TextureAtlas
{
int width;
int height;
Object2ObjectMap<AssetLocation, AtlasEntry> textures;
protected TextureAtlas(Vec2i bounds, Object2ObjectMap<AssetLocation, AtlasEntry> textures)
{
width = bounds.getX();
height = bounds.getY();
this.textures = textures;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
public AtlasEntry getTexture(AssetLocation texture)
{
return textures.getObject(texture);
}
public Iterator<AtlasEntry> getContents()
{
return ObjectIterators.unmodifiable(textures.values().iterator());
}
public static Builder create()
{
return new Builder();
}
public static class Builder
{
Set<AssetLocation> names = new ObjectOpenHashSet<>();
List<Record> records = new ObjectArrayList<>();
int pixelsUsed = 0;
public boolean add(AssetLocation location, int width, int height)
{
return add(location, width, height, 0);
}
public boolean add(AssetLocation location, int width, int height, int padding)
{
if(location == null || width <= 0 || height <= 0 || padding < 0 || !names.add(location)) return false;
records.add(new Record(location, width, height, padding));
pixelsUsed += (width + padding) * (height + padding);
return true;
}
private ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> stitch()
{
int textureWidth = 2;
int textureHeight = 2;
boolean height = false;
for(;textureHeight * textureWidth <= pixelsUsed;height = !height)
{
if(height) textureHeight *= 2;
else textureWidth *= 2;
}
records.sort(null);
int attempts = 0;
while(attempts < 50)
{
Slot slot = new Slot(0, 0, textureWidth, textureHeight);
boolean failed = false;
for(int i = 0,m=records.size();i<m;i++)
{
if(slot.addRecord(records.get(i))) continue;
failed = true;
break;
}
if(failed)
{
if(height) textureHeight *= 2;
else textureWidth *= 2;
attempts++;
height = !height;
continue;
}
Object2ObjectMap<AssetLocation, AtlasEntry> entries = new Object2ObjectLinkedOpenHashMap<>();
slot.build(entries::put);
return ObjectObjectPair.of(Vec2i.newVec(textureWidth, textureHeight), entries);
}
throw new IllegalStateException("Couldn't fit Texture Atlas after growing it 5 Times");
}
public void buildHollow(BiConsumer<Vec2i, Iterable<AtlasEntry>> builder)
{
ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> pairs = stitch();
builder.accept(pairs.getKey(), pairs.getValue().values());
}
public TextureAtlas build(BiConsumer<Vec2i, Iterable<AtlasEntry>> builder)
{
ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> pairs = stitch();
builder.accept(pairs.getKey(), pairs.getValue().values());
return new TextureAtlas(pairs.getKey(), pairs.getValue());
}
public TextureAtlas build()
{
ObjectObjectPair<Vec2i, Object2ObjectMap<AssetLocation, AtlasEntry>> pairs = stitch();
return new TextureAtlas(pairs.getKey(), pairs.getValue());
}
}
public static class AtlasEntry
{
AssetLocation location;
int x;
int y;
int width;
int height;
private AtlasEntry(Slot slot)
{
Record record = slot.record;
location = record == null ? null : record.getLocation();
x = slot.x;
y = slot.y;
width = record == null ? slot.width : record.width;
height = record == null ? slot.height : record.height;
}
public AssetLocation getLocation()
{
return location;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
}
private static class Slot
{
int x;
int y;
int width;
int height;
Record record;
Slot[] children = null;
public Slot(int x, int y, int width, int height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public boolean isLeaf()
{
return children == null;
}
public void build(BiConsumer<AssetLocation, AtlasEntry> builder)
{
if(!isLeaf())
{
for(int i = 0,m=children.length;i<m;i++)
{
children[i].build(builder);
}
return;
}
if(record == null) return;
builder.accept(record.getLocation(), new AtlasEntry(this));
}
public boolean addRecord(Record record)
{
if(isLeaf())
{
int rw = record.getWidth();
int rh = record.getHeight();
if(this.record != null || rw > width || rh > height) return false;
if(rw == width && rh == height)
{
this.record = record;
return true;
}
int p = record.getPadding();
int dw = width - rw;
int dh = height - rh;
children = new Slot[dw > 0 && dh > 0 ? 3 : 2];
children[0] = new Slot(x, y, rw, rh);
if(dw > 0 && dh > 0)
{
if(dw > dh)
{
children[1] = new Slot(x + rw + p, y, dw - p, rh);
children[2] = new Slot(x, y + rh + p, width, dh - p);
}
else
{
children[1] = new Slot(x, y + rh + p, rw, dh - p);
children[2] = new Slot(x + rw + p, y, dw - p, height);
}
}
else if(dw == 0) children[1] = new Slot(x, y + rh + p, rw, dh - p);
else if(dh == 0) children[1] = new Slot(x + rw + p, y, dw - p, rh);
return children[0].addRecord(record);
}
for(int i = 0,m=children.length;i<m;i++)
{
if(children[i].addRecord(record)) return true;
}
return false;
}
}
private static class Record implements Comparable<Record>
{
AssetLocation location;
int width;
int height;
int padding;
public Record(AssetLocation location, int width, int height, int padding)
{
this.location = location;
this.width = width;
this.height = height;
this.padding = padding;
}
public int getHeight()
{
return height;
}
public int getWidth()
{
return width;
}
public int getPadding()
{
return padding;
}
public AssetLocation getLocation()
{
return location;
}
private int getPixelCount()
{
return (width + padding) * (height + padding);
}
@Override
public int compareTo(Record o)
{
int result = Integer.compare(o.getPixelCount(), getPixelCount());
return result == 0 ? getLocation().compareTo(o.getLocation()) : result;
}
}
}

View File

@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.normal;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
@ -8,6 +8,9 @@ import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class DirectTexture extends AbstractTexture
{
BufferedImage image;
@ -93,7 +96,8 @@ public class DirectTexture extends AbstractTexture
@Override
public void reload()
{
TextureManager.INSTANCE.removeTexture(textureID);
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
}

View File

@ -1,4 +1,4 @@
package speiger.src.coreengine.rendering.textures;
package speiger.src.coreengine.rendering.textures.normal;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
@ -10,6 +10,8 @@ import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class SimpleTexture extends AbstractTexture
{
@ -66,8 +68,9 @@ public class SimpleTexture extends AbstractTexture
@Override
public void reload()
{
TextureManager.INSTANCE.removeTexture(textureID);
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
@Override

View File

@ -0,0 +1,109 @@
package speiger.src.coreengine.rendering.textures.stb;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.stb.STBImage;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class STBDirectTexture extends AbstractTexture
{
long imageData;
int width;
int height;
public STBDirectTexture(BufferedImage imageData)
{
this(convert(imageData), imageData.getWidth(), imageData.getHeight());
}
public STBDirectTexture(ByteBuffer imageData, int width, int height)
{
this(MemoryUtil.memAddress(imageData), width, height);
}
public STBDirectTexture(long imageData, int width, int height)
{
this.imageData = imageData;
this.width = width;
this.height = height;
loadTexture();
}
protected void loadTexture()
{
setTextureID(GL11.glGenTextures());
bindTexture();
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageData);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
}
@Override
public int getWidth()
{
return width;
}
@Override
public int getHeight()
{
return height;
}
@Override
public void reload()
{
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
@Override
public void deleteTexture()
{
if(imageData != 0)
{
STBImage.nstbi_image_free(imageData);
imageData = 0;
}
super.deleteTexture();
}
private static ByteBuffer convert(BufferedImage image)
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try
{
ImageIO.write(image, "png", stream);
ByteBuffer buffer = MemoryUtil.memAlloc(stream.size());
buffer.put(stream.toByteArray());
buffer.flip();
ByteBuffer result = STBImage.stbi_load_from_memory(buffer, new int[1], new int[1], new int[1], 4);
if(result == null) {
MemoryUtil.memFree(buffer);
throw new IOException("Could not load image: " + STBImage.stbi_failure_reason());
}
MemoryUtil.memFree(buffer);
return result;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,98 @@
package speiger.src.coreengine.rendering.textures.stb;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.stb.STBImage;
import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.rendering.textures.base.AbstractTexture;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public class STBTexture extends AbstractTexture
{
AssetLocation location;
long imageData;
int width;
int height;
public STBTexture(AssetLocation location)
{
this.location = location;
loadTexture();
}
private void loadTexture()
{
try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(location))
{
ByteBuffer buffer = asset.getCustom(ByteBuffer.class);
int[] width = new int[1];
int[] height = new int[1];
int[] fileChannels = new int[1];
ByteBuffer image = STBImage.stbi_load_from_memory(buffer, width, height, fileChannels, 4);
if(image == null) {
MemoryUtil.memFree(buffer);
throw new IOException("Could not load image: " + STBImage.stbi_failure_reason());
}
imageData = MemoryUtil.memAddress(image);
this.width = width[0];
this.height = height[0];
MemoryUtil.memFree(buffer);
}
catch(Exception e)
{
e.printStackTrace();
return;
}
setTextureID(GL11.glGenTextures());
bindTexture();
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageData);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
}
@Override
public int getWidth()
{
return width;
}
@Override
public int getHeight()
{
return height;
}
@Override
public void reload()
{
if(imageData != 0)
{
STBImage.nstbi_image_free(imageData);
imageData = 0;
}
int old = textureID;
loadTexture();
TextureManager.INSTANCE.removeTexture(old);
}
@Override
public void deleteTexture()
{
super.deleteTexture();
if(imageData != 0)
{
STBImage.nstbi_image_free(imageData);
imageData = 0;
}
}
}

View File

@ -12,7 +12,7 @@ import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.reloader.IReloadableResource;
import speiger.src.coreengine.rendering.input.window.Window;
import speiger.src.coreengine.rendering.textures.TextureManager;
import speiger.src.coreengine.rendering.textures.base.TextureManager;
public final class Cursor implements IReloadableResource
{

View File

@ -37,7 +37,7 @@ public class CollectionUtils
List<T>[] list = new List[size];
for(int i = 0;i<size;i++)
{
list[i] = new ObjectArrayList<T>();
list[i] = new ObjectArrayList<>();
}
return list;
}

View File

@ -6,10 +6,9 @@ import java.util.Set;
import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.maps.abstracts.AbstractInt2IntMap.BasicEntry;
import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap;
import speiger.src.collections.ints.maps.interfaces.Int2IntMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
import speiger.src.collections.ints.sets.IntAVLTreeSet;
import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.objects.lists.ObjectArrayList;
@ -166,7 +165,7 @@ public class DynamicDataManager<T>
}
return;
}
List<Entry<byte[]>> list = new ObjectArrayList<>();
List<IntObjectPair<byte[]>> list = new ObjectArrayList<>();
while(!changedSlots.isEmpty())
{
DataSlot start = null;
@ -196,7 +195,7 @@ public class DynamicDataManager<T>
bytesAllocated += start.getUsedBytes();
start = start.next;
}
list.add(new AbstractInt2ObjectMap.BasicEntry<>(byteOffset, data));
list.add(IntObjectPair.of(byteOffset, data));
}
if(!list.isEmpty())
{

View File

@ -2,10 +2,10 @@ package speiger.src.coreengine.utils.collections.managers.dynamic;
import java.util.List;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
public interface IDynamicDataHandler<T>
{
public byte[] toBytes(T entry);
public void uploadBytes(List<Entry<byte[]>> list, int newArraySize);
public void uploadBytes(List<IntObjectPair<byte[]>> list, int newArraySize);
}

View File

@ -10,10 +10,9 @@ import speiger.src.collections.ints.collections.IntIterator;
import speiger.src.collections.ints.functions.IntComparator;
import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntList;
import speiger.src.collections.ints.maps.abstracts.AbstractInt2ObjectMap.BasicEntry;
import speiger.src.collections.ints.maps.impl.hash.Int2IntOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2IntMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
import speiger.src.collections.ints.sets.IntAVLTreeSet;
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
import speiger.src.collections.ints.sets.IntSet;
@ -284,7 +283,7 @@ public class FixedDataManager
{
ObjectArrays.quickSort(slots);
List<FixedSlot> fixed = new ObjectArrayList<FixedSlot>();
List<Entry<byte[]>> data = new ObjectArrayList<Entry<byte[]>>();
List<IntObjectPair<byte[]>> data = new ObjectArrayList<>();
FixedSlot first = null;
FixedSlot last = null;
for(int i = 0,m=slots.length;i<m;i++)
@ -304,7 +303,7 @@ public class FixedDataManager
ByteBuffer buffer = ByteBuffer.allocate(fixed.size() * bytes).order(ByteOrder.nativeOrder());
handler.createData(buffer, fixed.size(), fixed);
fixed.clear();
data.add(new BasicEntry<byte[]>(first.index * bytes, buffer.array()));
data.add(IntObjectPair.of(first.index * bytes, buffer.array()));
i--;
first = last = null;
}
@ -313,7 +312,7 @@ public class FixedDataManager
ByteBuffer buffer = ByteBuffer.allocate(fixed.size() * bytes).order(ByteOrder.nativeOrder());
handler.createData(buffer, fixed.size(), fixed);
fixed.clear();
data.add(new BasicEntry<byte[]>(first.index * bytes, buffer.array()));
data.add(IntObjectPair.of(first.index * bytes, buffer.array()));
}
SLOTS.accept(slots);
return new FinishingTask(handler, data, size);
@ -339,10 +338,10 @@ public class FixedDataManager
static class FinishingTask implements Runnable
{
IFixedDataHandler handler;
List<Entry<byte[]>> data;
List<IntObjectPair<byte[]>> data;
int size;
public FinishingTask(IFixedDataHandler handler, List<Entry<byte[]>> data, int size)
public FinishingTask(IFixedDataHandler handler, List<IntObjectPair<byte[]>> data, int size)
{
this.handler = handler;
this.data = data;

View File

@ -3,11 +3,11 @@ package speiger.src.coreengine.utils.collections.managers.fixed;
import java.nio.ByteBuffer;
import java.util.List;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap.Entry;
import speiger.src.collections.ints.misc.pairs.IntObjectPair;
public interface IFixedDataHandler
{
public void createData(ByteBuffer data, int size, List<FixedSlot> slots);
public void updateData(List<Entry<byte[]>> list, int newSize);
public void updateData(List<IntObjectPair<byte[]>> list, int newSize);
}

View File

@ -8,6 +8,8 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@ -55,6 +57,32 @@ public class JsonUtil
}
}
public static void iterate(JsonElement element, Consumer<JsonObject> listener)
{
if(element.isJsonObject()) listener.accept(element.getAsJsonObject());
else if(element.isJsonArray())
{
JsonArray array = element.getAsJsonArray();
for(int i = 0,m=array.size();i<m;i++)
{
iterate(array.get(i), listener);
}
}
}
public static <T> void iterate(JsonElement element, T input, BiConsumer<JsonObject, T> listener)
{
if(element.isJsonObject()) listener.accept(element.getAsJsonObject(), input);
else if(element.isJsonArray())
{
JsonArray array = element.getAsJsonArray();
for(int i = 0,m=array.size();i<m;i++)
{
iterate(array.get(i), input, listener);
}
}
}
public static boolean getOrDefault(JsonObject obj, String name, boolean defaultValue)
{
JsonElement el = obj.get(name);
@ -103,6 +131,7 @@ public class JsonUtil
return el == null ? defaultValue : el.getAsString();
}
public static JsonArray toArray(byte[] values)
{
JsonArray array = new JsonArray();
@ -113,7 +142,28 @@ public class JsonUtil
return array;
}
public static byte[] parseArray(JsonArray array)
public static JsonArray toArray(float[] values)
{
JsonArray array = new JsonArray();
for(int i = 0,m=values.length;i<m;i++)
{
array.add(values[i]);
}
return array;
}
public static JsonArray toArray(Number[] values)
{
JsonArray array = new JsonArray();
for(int i = 0,m=values.length;i<m;i++)
{
array.add(values[i]);
}
return array;
}
public static byte[] parseByteArray(JsonArray array)
{
byte[] data = new byte[array.size()];
for(int i = 0,m=data.length;i<m;i++)
@ -122,4 +172,14 @@ public class JsonUtil
}
return data;
}
public static float[] parseFloatArray(JsonArray array)
{
float[] data = new float[array.size()];
for(int i = 0,m=data.length;i<m;i++)
{
data[i] = array.get(i).getAsFloat();
}
return data;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View File

@ -1,395 +0,0 @@
textureWidth=1024, textureHeight=512, base=39, fontHeight=50
letter=0, minX=0, minY=0, maxX=0, maxY=50, bold=0
letter=2, minX=0, minY=0, maxX=0, maxY=50, bold=0
letter=9, minX=0, minY=0, maxX=0, maxY=50, bold=0
letter=10, minX=0, minY=0, maxX=0, maxY=50, bold=0
letter=13, minX=0, minY=0, maxX=0, maxY=50, bold=0
letter=32, minX=0, minY=0, maxX=10, maxY=50, bold=0
letter=33, minX=10, minY=0, maxX=21, maxY=50, bold=0
letter=34, minX=21, minY=0, maxX=35, maxY=50, bold=0
letter=35, minX=35, minY=0, maxX=61, maxY=50, bold=0
letter=36, minX=61, minY=0, maxX=85, maxY=50, bold=0
letter=37, minX=85, minY=0, maxX=116, maxY=50, bold=0
letter=38, minX=116, minY=0, maxX=143, maxY=50, bold=0
letter=39, minX=143, minY=0, maxX=150, maxY=50, bold=0
letter=40, minX=150, minY=0, maxX=165, maxY=50, bold=0
letter=41, minX=165, minY=0, maxX=180, maxY=50, bold=0
letter=42, minX=180, minY=0, maxX=199, maxY=50, bold=0
letter=43, minX=199, minY=0, maxX=222, maxY=50, bold=0
letter=44, minX=222, minY=0, maxX=231, maxY=50, bold=0
letter=45, minX=231, minY=0, maxX=245, maxY=50, bold=0
letter=46, minX=245, minY=0, maxX=257, maxY=50, bold=0
letter=47, minX=257, minY=0, maxX=274, maxY=50, bold=0
letter=48, minX=274, minY=0, maxX=298, maxY=50, bold=0
letter=49, minX=298, minY=0, maxX=322, maxY=50, bold=0
letter=50, minX=322, minY=0, maxX=346, maxY=50, bold=0
letter=51, minX=346, minY=0, maxX=370, maxY=50, bold=0
letter=52, minX=370, minY=0, maxX=394, maxY=50, bold=0
letter=53, minX=394, minY=0, maxX=418, maxY=50, bold=0
letter=54, minX=418, minY=0, maxX=442, maxY=50, bold=0
letter=55, minX=442, minY=0, maxX=466, maxY=50, bold=0
letter=56, minX=466, minY=0, maxX=490, maxY=50, bold=0
letter=57, minX=490, minY=0, maxX=514, maxY=50, bold=0
letter=58, minX=514, minY=0, maxX=525, maxY=50, bold=0
letter=59, minX=525, minY=0, maxX=535, maxY=50, bold=0
letter=60, minX=535, minY=0, maxX=556, maxY=50, bold=0
letter=61, minX=556, minY=0, maxX=580, maxY=50, bold=0
letter=62, minX=580, minY=0, maxX=602, maxY=50, bold=0
letter=63, minX=602, minY=0, maxX=622, maxY=50, bold=0
letter=64, minX=622, minY=0, maxX=660, maxY=50, bold=0
letter=65, minX=660, minY=0, maxX=688, maxY=50, bold=0
letter=66, minX=688, minY=0, maxX=715, maxY=50, bold=0
letter=67, minX=715, minY=0, maxX=742, maxY=50, bold=0
letter=68, minX=742, minY=0, maxX=769, maxY=50, bold=0
letter=69, minX=769, minY=0, maxX=793, maxY=50, bold=0
letter=70, minX=793, minY=0, maxX=816, maxY=50, bold=0
letter=71, minX=816, minY=0, maxX=845, maxY=50, bold=0
letter=72, minX=845, minY=0, maxX=875, maxY=50, bold=0
letter=73, minX=875, minY=0, maxX=887, maxY=50, bold=0
letter=74, minX=887, minY=0, maxX=910, maxY=50, bold=0
letter=75, minX=910, minY=0, maxX=936, maxY=50, bold=0
letter=76, minX=936, minY=0, maxX=959, maxY=50, bold=0
letter=77, minX=959, minY=0, maxX=996, maxY=50, bold=0
letter=78, minX=0, minY=51, maxX=30, maxY=101, bold=0
letter=79, minX=30, minY=51, maxX=59, maxY=101, bold=0
letter=80, minX=59, minY=51, maxX=86, maxY=101, bold=0
letter=81, minX=86, minY=51, maxX=115, maxY=101, bold=0
letter=82, minX=115, minY=51, maxX=141, maxY=101, bold=0
letter=83, minX=141, minY=51, maxX=166, maxY=101, bold=0
letter=84, minX=166, minY=51, maxX=191, maxY=101, bold=0
letter=85, minX=191, minY=51, maxX=218, maxY=101, bold=0
letter=86, minX=218, minY=51, maxX=245, maxY=101, bold=0
letter=87, minX=245, minY=51, maxX=282, maxY=101, bold=0
letter=88, minX=282, minY=51, maxX=309, maxY=101, bold=0
letter=89, minX=309, minY=51, maxX=335, maxY=101, bold=0
letter=90, minX=335, minY=51, maxX=360, maxY=101, bold=0
letter=91, minX=360, minY=51, maxX=372, maxY=101, bold=0
letter=92, minX=372, minY=51, maxX=390, maxY=101, bold=0
letter=93, minX=390, minY=51, maxX=402, maxY=101, bold=0
letter=94, minX=402, minY=51, maxX=420, maxY=101, bold=0
letter=95, minX=420, minY=51, maxX=439, maxY=101, bold=0
letter=96, minX=439, minY=51, maxX=453, maxY=101, bold=0
letter=97, minX=453, minY=51, maxX=476, maxY=101, bold=0
letter=98, minX=476, minY=51, maxX=500, maxY=101, bold=0
letter=99, minX=500, minY=51, maxX=522, maxY=101, bold=0
letter=100, minX=522, minY=51, maxX=546, maxY=101, bold=0
letter=101, minX=546, minY=51, maxX=569, maxY=101, bold=0
letter=102, minX=569, minY=51, maxX=584, maxY=101, bold=0
letter=103, minX=584, minY=51, maxX=608, maxY=101, bold=0
letter=104, minX=608, minY=51, maxX=631, maxY=101, bold=0
letter=105, minX=631, minY=51, maxX=642, maxY=101, bold=0
letter=32, minX=642, minY=51, maxX=652, maxY=101, bold=0
letter=106, minX=652, minY=51, maxX=663, maxY=101, bold=0
letter=107, minX=663, minY=51, maxX=685, maxY=101, bold=0
letter=108, minX=685, minY=51, maxX=696, maxY=101, bold=0
letter=109, minX=696, minY=51, maxX=733, maxY=101, bold=0
letter=110, minX=733, minY=51, maxX=756, maxY=101, bold=0
letter=111, minX=756, minY=51, maxX=780, maxY=101, bold=0
letter=112, minX=780, minY=51, maxX=804, maxY=101, bold=0
letter=113, minX=804, minY=51, maxX=828, maxY=101, bold=0
letter=114, minX=828, minY=51, maxX=843, maxY=101, bold=0
letter=115, minX=843, minY=51, maxX=865, maxY=101, bold=0
letter=116, minX=865, minY=51, maxX=879, maxY=101, bold=0
letter=117, minX=879, minY=51, maxX=902, maxY=101, bold=0
letter=118, minX=902, minY=51, maxX=923, maxY=101, bold=0
letter=119, minX=923, minY=51, maxX=954, maxY=101, bold=0
letter=120, minX=954, minY=51, maxX=975, maxY=101, bold=0
letter=121, minX=975, minY=51, maxX=995, maxY=101, bold=0
letter=122, minX=995, minY=51, maxX=1016, maxY=101, bold=0
letter=123, minX=0, minY=102, maxX=14, maxY=152, bold=0
letter=124, minX=14, minY=102, maxX=25, maxY=152, bold=0
letter=125, minX=25, minY=102, maxX=39, maxY=152, bold=0
letter=126, minX=39, minY=102, maxX=67, maxY=152, bold=0
letter=160, minX=67, minY=102, maxX=77, maxY=152, bold=0
letter=161, minX=77, minY=102, maxX=88, maxY=152, bold=0
letter=162, minX=88, minY=102, maxX=112, maxY=152, bold=0
letter=163, minX=112, minY=102, maxX=137, maxY=152, bold=0
letter=164, minX=137, minY=102, maxX=166, maxY=152, bold=0
letter=165, minX=166, minY=102, maxX=188, maxY=152, bold=0
letter=166, minX=188, minY=102, maxX=198, maxY=152, bold=0
letter=167, minX=198, minY=102, maxX=224, maxY=152, bold=0
letter=168, minX=224, minY=102, maxX=242, maxY=152, bold=0
letter=169, minX=242, minY=102, maxX=275, maxY=152, bold=0
letter=170, minX=275, minY=102, maxX=294, maxY=152, bold=0
letter=171, minX=294, minY=102, maxX=314, maxY=152, bold=0
letter=172, minX=314, minY=102, maxX=337, maxY=152, bold=0
letter=173, minX=337, minY=102, maxX=351, maxY=152, bold=0
letter=174, minX=351, minY=102, maxX=384, maxY=152, bold=0
letter=175, minX=384, minY=102, maxX=404, maxY=152, bold=0
letter=176, minX=404, minY=102, maxX=420, maxY=152, bold=0
letter=177, minX=420, minY=102, maxX=443, maxY=152, bold=0
letter=178, minX=443, minY=102, maxX=459, maxY=152, bold=0
letter=179, minX=459, minY=102, maxX=475, maxY=152, bold=0
letter=180, minX=475, minY=102, maxX=489, maxY=152, bold=0
letter=181, minX=489, minY=102, maxX=514, maxY=152, bold=0
letter=182, minX=514, minY=102, maxX=535, maxY=152, bold=0
letter=183, minX=535, minY=102, maxX=547, maxY=152, bold=0
letter=184, minX=547, minY=102, maxX=558, maxY=152, bold=0
letter=185, minX=558, minY=102, maxX=574, maxY=152, bold=0
letter=186, minX=574, minY=102, maxX=593, maxY=152, bold=0
letter=187, minX=593, minY=102, maxX=613, maxY=152, bold=0
letter=188, minX=613, minY=102, maxX=644, maxY=152, bold=0
letter=189, minX=644, minY=102, maxX=676, maxY=152, bold=0
letter=190, minX=676, minY=102, maxX=709, maxY=152, bold=0
letter=191, minX=709, minY=102, maxX=729, maxY=152, bold=0
letter=192, minX=729, minY=102, maxX=757, maxY=152, bold=0
letter=193, minX=757, minY=102, maxX=785, maxY=152, bold=0
letter=194, minX=785, minY=102, maxX=813, maxY=152, bold=0
letter=195, minX=813, minY=102, maxX=841, maxY=152, bold=0
letter=196, minX=841, minY=102, maxX=869, maxY=152, bold=0
letter=197, minX=869, minY=102, maxX=897, maxY=152, bold=0
letter=198, minX=897, minY=102, maxX=936, maxY=152, bold=0
letter=199, minX=936, minY=102, maxX=963, maxY=152, bold=0
letter=200, minX=963, minY=102, maxX=987, maxY=152, bold=0
letter=201, minX=987, minY=102, maxX=1011, maxY=152, bold=0
letter=202, minX=0, minY=153, maxX=24, maxY=203, bold=0
letter=203, minX=24, minY=153, maxX=48, maxY=203, bold=0
letter=204, minX=48, minY=153, maxX=60, maxY=203, bold=0
letter=205, minX=60, minY=153, maxX=72, maxY=203, bold=0
letter=206, minX=72, minY=153, maxX=84, maxY=203, bold=0
letter=207, minX=84, minY=153, maxX=96, maxY=203, bold=0
letter=208, minX=96, minY=153, maxX=124, maxY=203, bold=0
letter=209, minX=124, minY=153, maxX=154, maxY=203, bold=0
letter=210, minX=154, minY=153, maxX=183, maxY=203, bold=0
letter=211, minX=183, minY=153, maxX=212, maxY=203, bold=0
letter=212, minX=212, minY=153, maxX=241, maxY=203, bold=0
letter=213, minX=241, minY=153, maxX=270, maxY=203, bold=0
letter=214, minX=270, minY=153, maxX=299, maxY=203, bold=0
letter=215, minX=299, minY=153, maxX=321, maxY=203, bold=0
letter=216, minX=321, minY=153, maxX=350, maxY=203, bold=0
letter=217, minX=350, minY=153, maxX=377, maxY=203, bold=0
letter=218, minX=377, minY=153, maxX=404, maxY=203, bold=0
letter=219, minX=404, minY=153, maxX=431, maxY=203, bold=0
letter=220, minX=431, minY=153, maxX=458, maxY=203, bold=0
letter=221, minX=458, minY=153, maxX=484, maxY=203, bold=0
letter=222, minX=484, minY=153, maxX=509, maxY=203, bold=0
letter=223, minX=509, minY=153, maxX=535, maxY=203, bold=0
letter=224, minX=535, minY=153, maxX=558, maxY=203, bold=0
letter=225, minX=558, minY=153, maxX=581, maxY=203, bold=0
letter=226, minX=581, minY=153, maxX=604, maxY=203, bold=0
letter=227, minX=604, minY=153, maxX=627, maxY=203, bold=0
letter=228, minX=627, minY=153, maxX=650, maxY=203, bold=0
letter=229, minX=650, minY=153, maxX=673, maxY=203, bold=0
letter=230, minX=673, minY=153, maxX=708, maxY=203, bold=0
letter=231, minX=708, minY=153, maxX=730, maxY=203, bold=0
letter=232, minX=730, minY=153, maxX=753, maxY=203, bold=0
letter=233, minX=753, minY=153, maxX=776, maxY=203, bold=0
letter=234, minX=776, minY=153, maxX=799, maxY=203, bold=0
letter=235, minX=799, minY=153, maxX=822, maxY=203, bold=0
letter=236, minX=822, minY=153, maxX=833, maxY=203, bold=0
letter=237, minX=833, minY=153, maxX=844, maxY=203, bold=0
letter=238, minX=844, minY=153, maxX=855, maxY=203, bold=0
letter=239, minX=855, minY=153, maxX=866, maxY=203, bold=0
letter=240, minX=866, minY=153, maxX=890, maxY=203, bold=0
letter=241, minX=890, minY=153, maxX=913, maxY=203, bold=0
letter=242, minX=913, minY=153, maxX=937, maxY=203, bold=0
letter=243, minX=937, minY=153, maxX=961, maxY=203, bold=0
letter=244, minX=961, minY=153, maxX=985, maxY=203, bold=0
letter=245, minX=985, minY=153, maxX=1009, maxY=203, bold=0
letter=246, minX=0, minY=204, maxX=24, maxY=254, bold=0
letter=247, minX=24, minY=204, maxX=48, maxY=254, bold=0
letter=248, minX=48, minY=204, maxX=72, maxY=254, bold=0
letter=249, minX=72, minY=204, maxX=95, maxY=254, bold=0
letter=250, minX=95, minY=204, maxX=118, maxY=254, bold=0
letter=251, minX=118, minY=204, maxX=141, maxY=254, bold=0
letter=252, minX=141, minY=204, maxX=164, maxY=254, bold=0
letter=253, minX=164, minY=204, maxX=184, maxY=254, bold=0
letter=254, minX=184, minY=204, maxX=208, maxY=254, bold=0
letter=255, minX=208, minY=204, maxX=228, maxY=254, bold=0
letter=0, minX=0, minY=0, maxX=0, maxY=50, bold=1
letter=2, minX=0, minY=0, maxX=0, maxY=50, bold=1
letter=9, minX=0, minY=0, maxX=0, maxY=50, bold=1
letter=10, minX=0, minY=0, maxX=0, maxY=50, bold=1
letter=13, minX=0, minY=0, maxX=0, maxY=50, bold=1
letter=32, minX=228, minY=204, maxX=240, maxY=254, bold=1
letter=33, minX=240, minY=204, maxX=253, maxY=254, bold=1
letter=34, minX=253, minY=204, maxX=269, maxY=254, bold=1
letter=35, minX=269, minY=204, maxX=297, maxY=254, bold=1
letter=36, minX=297, minY=204, maxX=323, maxY=254, bold=1
letter=37, minX=323, minY=204, maxX=356, maxY=254, bold=1
letter=38, minX=356, minY=204, maxX=385, maxY=254, bold=1
letter=39, minX=385, minY=204, maxX=394, maxY=254, bold=1
letter=40, minX=394, minY=204, maxX=411, maxY=254, bold=1
letter=41, minX=411, minY=204, maxX=428, maxY=254, bold=1
letter=42, minX=428, minY=204, maxX=449, maxY=254, bold=1
letter=43, minX=449, minY=204, maxX=474, maxY=254, bold=1
letter=44, minX=474, minY=204, maxX=485, maxY=254, bold=1
letter=45, minX=485, minY=204, maxX=501, maxY=254, bold=1
letter=46, minX=501, minY=204, maxX=515, maxY=254, bold=1
letter=47, minX=515, minY=204, maxX=534, maxY=254, bold=1
letter=48, minX=534, minY=204, maxX=560, maxY=254, bold=1
letter=49, minX=560, minY=204, maxX=586, maxY=254, bold=1
letter=50, minX=586, minY=204, maxX=612, maxY=254, bold=1
letter=51, minX=612, minY=204, maxX=638, maxY=254, bold=1
letter=52, minX=638, minY=204, maxX=664, maxY=254, bold=1
letter=53, minX=664, minY=204, maxX=690, maxY=254, bold=1
letter=54, minX=690, minY=204, maxX=716, maxY=254, bold=1
letter=55, minX=716, minY=204, maxX=742, maxY=254, bold=1
letter=56, minX=742, minY=204, maxX=768, maxY=254, bold=1
letter=57, minX=768, minY=204, maxX=794, maxY=254, bold=1
letter=58, minX=794, minY=204, maxX=807, maxY=254, bold=1
letter=59, minX=807, minY=204, maxX=819, maxY=254, bold=1
letter=60, minX=819, minY=204, maxX=842, maxY=254, bold=1
letter=61, minX=842, minY=204, maxX=868, maxY=254, bold=1
letter=62, minX=868, minY=204, maxX=892, maxY=254, bold=1
letter=63, minX=892, minY=204, maxX=914, maxY=254, bold=1
letter=64, minX=914, minY=204, maxX=954, maxY=254, bold=1
letter=65, minX=954, minY=204, maxX=984, maxY=254, bold=1
letter=66, minX=984, minY=204, maxX=1013, maxY=254, bold=1
letter=67, minX=0, minY=255, maxX=29, maxY=305, bold=1
letter=68, minX=29, minY=255, maxX=58, maxY=305, bold=1
letter=69, minX=58, minY=255, maxX=84, maxY=305, bold=1
letter=70, minX=84, minY=255, maxX=109, maxY=305, bold=1
letter=71, minX=109, minY=255, maxX=140, maxY=305, bold=1
letter=72, minX=140, minY=255, maxX=172, maxY=305, bold=1
letter=73, minX=172, minY=255, maxX=186, maxY=305, bold=1
letter=74, minX=186, minY=255, maxX=211, maxY=305, bold=1
letter=75, minX=211, minY=255, maxX=239, maxY=305, bold=1
letter=76, minX=239, minY=255, maxX=264, maxY=305, bold=1
letter=77, minX=264, minY=255, maxX=303, maxY=305, bold=1
letter=78, minX=303, minY=255, maxX=335, maxY=305, bold=1
letter=79, minX=335, minY=255, maxX=366, maxY=305, bold=1
letter=80, minX=366, minY=255, maxX=395, maxY=305, bold=1
letter=81, minX=395, minY=255, maxX=426, maxY=305, bold=1
letter=82, minX=426, minY=255, maxX=454, maxY=305, bold=1
letter=83, minX=454, minY=255, maxX=481, maxY=305, bold=1
letter=84, minX=481, minY=255, maxX=508, maxY=305, bold=1
letter=85, minX=508, minY=255, maxX=537, maxY=305, bold=1
letter=86, minX=537, minY=255, maxX=566, maxY=305, bold=1
letter=87, minX=566, minY=255, maxX=605, maxY=305, bold=1
letter=88, minX=605, minY=255, maxX=634, maxY=305, bold=1
letter=89, minX=634, minY=255, maxX=662, maxY=305, bold=1
letter=90, minX=662, minY=255, maxX=689, maxY=305, bold=1
letter=91, minX=689, minY=255, maxX=703, maxY=305, bold=1
letter=92, minX=703, minY=255, maxX=723, maxY=305, bold=1
letter=93, minX=723, minY=255, maxX=737, maxY=305, bold=1
letter=94, minX=737, minY=255, maxX=757, maxY=305, bold=1
letter=95, minX=757, minY=255, maxX=778, maxY=305, bold=1
letter=96, minX=778, minY=255, maxX=794, maxY=305, bold=1
letter=97, minX=794, minY=255, maxX=819, maxY=305, bold=1
letter=98, minX=819, minY=255, maxX=845, maxY=305, bold=1
letter=99, minX=845, minY=255, maxX=869, maxY=305, bold=1
letter=100, minX=869, minY=255, maxX=895, maxY=305, bold=1
letter=101, minX=895, minY=255, maxX=920, maxY=305, bold=1
letter=102, minX=920, minY=255, maxX=937, maxY=305, bold=1
letter=103, minX=937, minY=255, maxX=963, maxY=305, bold=1
letter=104, minX=963, minY=255, maxX=988, maxY=305, bold=1
letter=105, minX=988, minY=255, maxX=1001, maxY=305, bold=1
letter=32, minX=1001, minY=255, maxX=1013, maxY=305, bold=1
letter=106, minX=0, minY=306, maxX=13, maxY=356, bold=1
letter=107, minX=13, minY=306, maxX=37, maxY=356, bold=1
letter=108, minX=37, minY=306, maxX=50, maxY=356, bold=1
letter=109, minX=50, minY=306, maxX=89, maxY=356, bold=1
letter=110, minX=89, minY=306, maxX=114, maxY=356, bold=1
letter=111, minX=114, minY=306, maxX=140, maxY=356, bold=1
letter=112, minX=140, minY=306, maxX=166, maxY=356, bold=1
letter=113, minX=166, minY=306, maxX=192, maxY=356, bold=1
letter=114, minX=192, minY=306, maxX=209, maxY=356, bold=1
letter=115, minX=209, minY=306, maxX=233, maxY=356, bold=1
letter=116, minX=233, minY=306, maxX=249, maxY=356, bold=1
letter=117, minX=249, minY=306, maxX=274, maxY=356, bold=1
letter=118, minX=274, minY=306, maxX=297, maxY=356, bold=1
letter=119, minX=297, minY=306, maxX=330, maxY=356, bold=1
letter=120, minX=330, minY=306, maxX=353, maxY=356, bold=1
letter=121, minX=353, minY=306, maxX=375, maxY=356, bold=1
letter=122, minX=375, minY=306, maxX=398, maxY=356, bold=1
letter=123, minX=398, minY=306, maxX=414, maxY=356, bold=1
letter=124, minX=414, minY=306, maxX=427, maxY=356, bold=1
letter=125, minX=427, minY=306, maxX=443, maxY=356, bold=1
letter=126, minX=443, minY=306, maxX=473, maxY=356, bold=1
letter=160, minX=473, minY=306, maxX=485, maxY=356, bold=1
letter=161, minX=485, minY=306, maxX=498, maxY=356, bold=1
letter=162, minX=498, minY=306, maxX=524, maxY=356, bold=1
letter=163, minX=524, minY=306, maxX=551, maxY=356, bold=1
letter=164, minX=551, minY=306, maxX=582, maxY=356, bold=1
letter=165, minX=582, minY=306, maxX=606, maxY=356, bold=1
letter=166, minX=606, minY=306, maxX=618, maxY=356, bold=1
letter=167, minX=618, minY=306, maxX=646, maxY=356, bold=1
letter=168, minX=646, minY=306, maxX=666, maxY=356, bold=1
letter=169, minX=666, minY=306, maxX=701, maxY=356, bold=1
letter=170, minX=701, minY=306, maxX=722, maxY=356, bold=1
letter=171, minX=722, minY=306, maxX=744, maxY=356, bold=1
letter=172, minX=744, minY=306, maxX=769, maxY=356, bold=1
letter=173, minX=769, minY=306, maxX=785, maxY=356, bold=1
letter=174, minX=785, minY=306, maxX=820, maxY=356, bold=1
letter=175, minX=820, minY=306, maxX=842, maxY=356, bold=1
letter=176, minX=842, minY=306, maxX=860, maxY=356, bold=1
letter=177, minX=860, minY=306, maxX=885, maxY=356, bold=1
letter=178, minX=885, minY=306, maxX=903, maxY=356, bold=1
letter=179, minX=903, minY=306, maxX=921, maxY=356, bold=1
letter=180, minX=921, minY=306, maxX=937, maxY=356, bold=1
letter=181, minX=937, minY=306, maxX=964, maxY=356, bold=1
letter=182, minX=964, minY=306, maxX=987, maxY=356, bold=1
letter=183, minX=987, minY=306, maxX=1001, maxY=356, bold=1
letter=184, minX=1001, minY=306, maxX=1014, maxY=356, bold=1
letter=185, minX=0, minY=357, maxX=18, maxY=407, bold=1
letter=186, minX=18, minY=357, maxX=39, maxY=407, bold=1
letter=187, minX=39, minY=357, maxX=61, maxY=407, bold=1
letter=188, minX=61, minY=357, maxX=94, maxY=407, bold=1
letter=189, minX=94, minY=357, maxX=128, maxY=407, bold=1
letter=190, minX=128, minY=357, maxX=163, maxY=407, bold=1
letter=191, minX=163, minY=357, maxX=185, maxY=407, bold=1
letter=192, minX=185, minY=357, maxX=215, maxY=407, bold=1
letter=193, minX=215, minY=357, maxX=245, maxY=407, bold=1
letter=194, minX=245, minY=357, maxX=275, maxY=407, bold=1
letter=195, minX=275, minY=357, maxX=305, maxY=407, bold=1
letter=196, minX=305, minY=357, maxX=335, maxY=407, bold=1
letter=197, minX=335, minY=357, maxX=365, maxY=407, bold=1
letter=198, minX=365, minY=357, maxX=406, maxY=407, bold=1
letter=199, minX=406, minY=357, maxX=435, maxY=407, bold=1
letter=200, minX=435, minY=357, maxX=461, maxY=407, bold=1
letter=201, minX=461, minY=357, maxX=487, maxY=407, bold=1
letter=202, minX=487, minY=357, maxX=513, maxY=407, bold=1
letter=203, minX=513, minY=357, maxX=539, maxY=407, bold=1
letter=204, minX=539, minY=357, maxX=553, maxY=407, bold=1
letter=205, minX=553, minY=357, maxX=567, maxY=407, bold=1
letter=206, minX=567, minY=357, maxX=581, maxY=407, bold=1
letter=207, minX=581, minY=357, maxX=595, maxY=407, bold=1
letter=208, minX=595, minY=357, maxX=625, maxY=407, bold=1
letter=209, minX=625, minY=357, maxX=657, maxY=407, bold=1
letter=210, minX=657, minY=357, maxX=688, maxY=407, bold=1
letter=211, minX=688, minY=357, maxX=719, maxY=407, bold=1
letter=212, minX=719, minY=357, maxX=750, maxY=407, bold=1
letter=213, minX=750, minY=357, maxX=781, maxY=407, bold=1
letter=214, minX=781, minY=357, maxX=812, maxY=407, bold=1
letter=215, minX=812, minY=357, maxX=836, maxY=407, bold=1
letter=216, minX=836, minY=357, maxX=867, maxY=407, bold=1
letter=217, minX=867, minY=357, maxX=896, maxY=407, bold=1
letter=218, minX=896, minY=357, maxX=925, maxY=407, bold=1
letter=219, minX=925, minY=357, maxX=954, maxY=407, bold=1
letter=220, minX=954, minY=357, maxX=983, maxY=407, bold=1
letter=221, minX=983, minY=357, maxX=1011, maxY=407, bold=1
letter=222, minX=0, minY=408, maxX=27, maxY=458, bold=1
letter=223, minX=27, minY=408, maxX=55, maxY=458, bold=1
letter=224, minX=55, minY=408, maxX=80, maxY=458, bold=1
letter=225, minX=80, minY=408, maxX=105, maxY=458, bold=1
letter=226, minX=105, minY=408, maxX=130, maxY=458, bold=1
letter=227, minX=130, minY=408, maxX=155, maxY=458, bold=1
letter=228, minX=155, minY=408, maxX=180, maxY=458, bold=1
letter=229, minX=180, minY=408, maxX=205, maxY=458, bold=1
letter=230, minX=205, minY=408, maxX=242, maxY=458, bold=1
letter=231, minX=242, minY=408, maxX=266, maxY=458, bold=1
letter=232, minX=266, minY=408, maxX=291, maxY=458, bold=1
letter=233, minX=291, minY=408, maxX=316, maxY=458, bold=1
letter=234, minX=316, minY=408, maxX=341, maxY=458, bold=1
letter=235, minX=341, minY=408, maxX=366, maxY=458, bold=1
letter=236, minX=366, minY=408, maxX=379, maxY=458, bold=1
letter=237, minX=379, minY=408, maxX=392, maxY=458, bold=1
letter=238, minX=392, minY=408, maxX=405, maxY=458, bold=1
letter=239, minX=405, minY=408, maxX=418, maxY=458, bold=1
letter=240, minX=418, minY=408, maxX=444, maxY=458, bold=1
letter=241, minX=444, minY=408, maxX=469, maxY=458, bold=1
letter=242, minX=469, minY=408, maxX=495, maxY=458, bold=1
letter=243, minX=495, minY=408, maxX=521, maxY=458, bold=1
letter=244, minX=521, minY=408, maxX=547, maxY=458, bold=1
letter=245, minX=547, minY=408, maxX=573, maxY=458, bold=1
letter=246, minX=573, minY=408, maxX=599, maxY=458, bold=1
letter=247, minX=599, minY=408, maxX=625, maxY=458, bold=1
letter=248, minX=625, minY=408, maxX=651, maxY=458, bold=1
letter=249, minX=651, minY=408, maxX=676, maxY=458, bold=1
letter=250, minX=676, minY=408, maxX=701, maxY=458, bold=1
letter=251, minX=701, minY=408, maxX=726, maxY=458, bold=1
letter=252, minX=726, minY=408, maxX=751, maxY=458, bold=1
letter=253, minX=751, minY=408, maxX=773, maxY=458, bold=1
letter=254, minX=773, minY=408, maxX=799, maxY=458, bold=1
letter=255, minX=799, minY=408, maxX=821, maxY=458, bold=1

View File

@ -0,0 +1,405 @@
{
"type": "bitmap",
"file": "base:font/roboto_texture.png",
"info": {
"width": 2048,
"height": 1024,
"base": 80,
"charHeight": 100,
"tabs": 4
},
"chars": [
{"char": 198, "minX": 0, "minY": 0, "maxX": 79, "maxY": 100, "bold": true},
{"char": 64, "minX": 79, "minY": 0, "maxX": 154, "maxY": 100, "bold": true},
{"char": 109, "minX": 154, "minY": 0, "maxX": 227, "maxY": 100, "bold": true},
{"char": 87, "minX": 227, "minY": 0, "maxX": 300, "maxY": 100, "bold": true},
{"char": 77, "minX": 300, "minY": 0, "maxX": 373, "maxY": 100, "bold": true},
{"char": 198, "minX": 373, "minY": 0, "maxX": 448, "maxY": 96, "bold": false},
{"char": 64, "minX": 448, "minY": 0, "maxX": 520, "maxY": 96, "bold": false},
{"char": 109, "minX": 520, "minY": 0, "maxX": 590, "maxY": 96, "bold": false},
{"char": 87, "minX": 590, "minY": 0, "maxX": 660, "maxY": 96, "bold": false},
{"char": 77, "minX": 660, "minY": 0, "maxX": 730, "maxY": 96, "bold": false},
{"char": 230, "minX": 730, "minY": 0, "maxX": 798, "maxY": 96, "bold": false},
{"char": 190, "minX": 798, "minY": 0, "maxX": 861, "maxY": 96, "bold": false},
{"char": 174, "minX": 861, "minY": 0, "maxX": 924, "maxY": 96, "bold": false},
{"char": 169, "minX": 924, "minY": 0, "maxX": 987, "maxY": 96, "bold": false},
{"char": 189, "minX": 987, "minY": 0, "maxX": 1049, "maxY": 96, "bold": false},
{"char": 119, "minX": 1049, "minY": 0, "maxX": 1108, "maxY": 96, "bold": false},
{"char": 37, "minX": 1108, "minY": 0, "maxX": 1167, "maxY": 96, "bold": false},
{"char": 188, "minX": 1167, "minY": 0, "maxX": 1225, "maxY": 96, "bold": false},
{"char": 209, "minX": 1225, "minY": 0, "maxX": 1282, "maxY": 96, "bold": false},
{"char": 72, "minX": 1282, "minY": 0, "maxX": 1339, "maxY": 96, "bold": false},
{"char": 78, "minX": 1339, "minY": 0, "maxX": 1396, "maxY": 96, "bold": false},
{"char": 164, "minX": 1396, "minY": 0, "maxX": 1452, "maxY": 96, "bold": false},
{"char": 210, "minX": 1452, "minY": 0, "maxX": 1507, "maxY": 96, "bold": false},
{"char": 211, "minX": 1507, "minY": 0, "maxX": 1562, "maxY": 96, "bold": false},
{"char": 212, "minX": 1562, "minY": 0, "maxX": 1617, "maxY": 96, "bold": false},
{"char": 213, "minX": 1617, "minY": 0, "maxX": 1672, "maxY": 96, "bold": false},
{"char": 214, "minX": 1672, "minY": 0, "maxX": 1727, "maxY": 96, "bold": false},
{"char": 216, "minX": 1727, "minY": 0, "maxX": 1782, "maxY": 96, "bold": false},
{"char": 79, "minX": 1782, "minY": 0, "maxX": 1837, "maxY": 96, "bold": false},
{"char": 81, "minX": 1837, "minY": 0, "maxX": 1892, "maxY": 96, "bold": false},
{"char": 71, "minX": 1892, "minY": 0, "maxX": 1946, "maxY": 96, "bold": false},
{"char": 196, "minX": 1946, "minY": 0, "maxX": 1999, "maxY": 96, "bold": false},
{"char": 221, "minX": 1999, "minY": 0, "maxX": 2048, "maxY": 96, "bold": false},
{"char": 230, "minX": 0, "minY": 100, "maxX": 71, "maxY": 200, "bold": true},
{"char": 190, "minX": 71, "minY": 100, "maxX": 137, "maxY": 200, "bold": true},
{"char": 174, "minX": 137, "minY": 100, "maxX": 203, "maxY": 200, "bold": true},
{"char": 169, "minX": 203, "minY": 100, "maxX": 269, "maxY": 200, "bold": true},
{"char": 189, "minX": 269, "minY": 100, "maxX": 334, "maxY": 200, "bold": true},
{"char": 119, "minX": 334, "minY": 100, "maxX": 396, "maxY": 200, "bold": true},
{"char": 37, "minX": 396, "minY": 100, "maxX": 458, "maxY": 200, "bold": true},
{"char": 188, "minX": 458, "minY": 100, "maxX": 519, "maxY": 200, "bold": true},
{"char": 209, "minX": 519, "minY": 100, "maxX": 579, "maxY": 200, "bold": true},
{"char": 72, "minX": 579, "minY": 100, "maxX": 639, "maxY": 200, "bold": true},
{"char": 78, "minX": 639, "minY": 100, "maxX": 699, "maxY": 200, "bold": true},
{"char": 164, "minX": 699, "minY": 100, "maxX": 758, "maxY": 200, "bold": true},
{"char": 210, "minX": 758, "minY": 100, "maxX": 816, "maxY": 200, "bold": true},
{"char": 211, "minX": 816, "minY": 100, "maxX": 874, "maxY": 200, "bold": true},
{"char": 212, "minX": 874, "minY": 100, "maxX": 932, "maxY": 200, "bold": true},
{"char": 213, "minX": 932, "minY": 100, "maxX": 990, "maxY": 200, "bold": true},
{"char": 214, "minX": 990, "minY": 100, "maxX": 1048, "maxY": 200, "bold": true},
{"char": 216, "minX": 1048, "minY": 100, "maxX": 1106, "maxY": 200, "bold": true},
{"char": 196, "minX": 1106, "minY": 100, "maxX": 1164, "maxY": 200, "bold": true},
{"char": 197, "minX": 1164, "minY": 100, "maxX": 1222, "maxY": 200, "bold": true},
{"char": 192, "minX": 1222, "minY": 100, "maxX": 1280, "maxY": 200, "bold": true},
{"char": 193, "minX": 1280, "minY": 100, "maxX": 1338, "maxY": 200, "bold": true},
{"char": 194, "minX": 1338, "minY": 100, "maxX": 1396, "maxY": 200, "bold": true},
{"char": 195, "minX": 1396, "minY": 100, "maxX": 1454, "maxY": 200, "bold": true},
{"char": 79, "minX": 1454, "minY": 100, "maxX": 1512, "maxY": 200, "bold": true},
{"char": 81, "minX": 1512, "minY": 100, "maxX": 1570, "maxY": 200, "bold": true},
{"char": 65, "minX": 1570, "minY": 100, "maxX": 1628, "maxY": 200, "bold": true},
{"char": 71, "minX": 1628, "minY": 100, "maxX": 1685, "maxY": 200, "bold": true},
{"char": 126, "minX": 1685, "minY": 100, "maxX": 1741, "maxY": 200, "bold": true},
{"char": 220, "minX": 1741, "minY": 100, "maxX": 1796, "maxY": 200, "bold": true},
{"char": 217, "minX": 1796, "minY": 100, "maxX": 1851, "maxY": 200, "bold": true},
{"char": 218, "minX": 1851, "minY": 100, "maxX": 1906, "maxY": 200, "bold": true},
{"char": 219, "minX": 1906, "minY": 100, "maxX": 1961, "maxY": 200, "bold": true},
{"char": 199, "minX": 1961, "minY": 100, "maxX": 2016, "maxY": 200, "bold": true},
{"char": 239, "minX": 2016, "minY": 100, "maxX": 2048, "maxY": 200, "bold": true},
{"char": 85, "minX": 0, "minY": 200, "maxX": 55, "maxY": 300, "bold": true},
{"char": 86, "minX": 55, "minY": 200, "maxX": 110, "maxY": 300, "bold": true},
{"char": 67, "minX": 110, "minY": 200, "maxX": 165, "maxY": 300, "bold": true},
{"char": 68, "minX": 165, "minY": 200, "maxX": 220, "maxY": 300, "bold": true},
{"char": 221, "minX": 220, "minY": 200, "maxX": 274, "maxY": 300, "bold": true},
{"char": 208, "minX": 274, "minY": 200, "maxX": 328, "maxY": 300, "bold": true},
{"char": 80, "minX": 328, "minY": 200, "maxX": 382, "maxY": 300, "bold": true},
{"char": 88, "minX": 382, "minY": 200, "maxX": 436, "maxY": 300, "bold": true},
{"char": 89, "minX": 436, "minY": 200, "maxX": 490, "maxY": 300, "bold": true},
{"char": 38, "minX": 490, "minY": 200, "maxX": 544, "maxY": 300, "bold": true},
{"char": 167, "minX": 544, "minY": 200, "maxX": 597, "maxY": 300, "bold": true},
{"char": 82, "minX": 597, "minY": 200, "maxX": 650, "maxY": 300, "bold": true},
{"char": 75, "minX": 650, "minY": 200, "maxX": 703, "maxY": 300, "bold": true},
{"char": 66, "minX": 703, "minY": 200, "maxX": 756, "maxY": 300, "bold": true},
{"char": 223, "minX": 756, "minY": 200, "maxX": 808, "maxY": 300, "bold": true},
{"char": 84, "minX": 808, "minY": 200, "maxX": 860, "maxY": 300, "bold": true},
{"char": 35, "minX": 860, "minY": 200, "maxX": 912, "maxY": 300, "bold": true},
{"char": 222, "minX": 912, "minY": 200, "maxX": 963, "maxY": 300, "bold": true},
{"char": 83, "minX": 963, "minY": 200, "maxX": 1014, "maxY": 300, "bold": true},
{"char": 90, "minX": 1014, "minY": 200, "maxX": 1065, "maxY": 300, "bold": true},
{"char": 197, "minX": 1065, "minY": 200, "maxX": 1118, "maxY": 296, "bold": false},
{"char": 192, "minX": 1118, "minY": 200, "maxX": 1171, "maxY": 296, "bold": false},
{"char": 193, "minX": 1171, "minY": 200, "maxX": 1224, "maxY": 296, "bold": false},
{"char": 194, "minX": 1224, "minY": 200, "maxX": 1277, "maxY": 296, "bold": false},
{"char": 195, "minX": 1277, "minY": 200, "maxX": 1330, "maxY": 296, "bold": false},
{"char": 126, "minX": 1330, "minY": 200, "maxX": 1383, "maxY": 296, "bold": false},
{"char": 65, "minX": 1383, "minY": 200, "maxX": 1436, "maxY": 296, "bold": false},
{"char": 220, "minX": 1436, "minY": 200, "maxX": 1488, "maxY": 296, "bold": false},
{"char": 217, "minX": 1488, "minY": 200, "maxX": 1540, "maxY": 296, "bold": false},
{"char": 218, "minX": 1540, "minY": 200, "maxX": 1592, "maxY": 296, "bold": false},
{"char": 219, "minX": 1592, "minY": 200, "maxX": 1644, "maxY": 296, "bold": false},
{"char": 199, "minX": 1644, "minY": 200, "maxX": 1696, "maxY": 296, "bold": false},
{"char": 85, "minX": 1696, "minY": 200, "maxX": 1748, "maxY": 296, "bold": false},
{"char": 86, "minX": 1748, "minY": 200, "maxX": 1800, "maxY": 296, "bold": false},
{"char": 67, "minX": 1800, "minY": 200, "maxX": 1852, "maxY": 296, "bold": false},
{"char": 68, "minX": 1852, "minY": 200, "maxX": 1904, "maxY": 296, "bold": false},
{"char": 208, "minX": 1904, "minY": 200, "maxX": 1955, "maxY": 296, "bold": false},
{"char": 80, "minX": 1955, "minY": 200, "maxX": 2006, "maxY": 296, "bold": false},
{"char": 231, "minX": 2006, "minY": 200, "maxX": 2048, "maxY": 296, "bold": false},
{"char": 240, "minX": 0, "minY": 300, "maxX": 50, "maxY": 400, "bold": true},
{"char": 181, "minX": 50, "minY": 300, "maxX": 100, "maxY": 400, "bold": true},
{"char": 163, "minX": 100, "minY": 300, "maxX": 150, "maxY": 400, "bold": true},
{"char": 244, "minX": 150, "minY": 300, "maxX": 199, "maxY": 400, "bold": true},
{"char": 245, "minX": 199, "minY": 300, "maxX": 248, "maxY": 400, "bold": true},
{"char": 246, "minX": 248, "minY": 300, "maxX": 297, "maxY": 400, "bold": true},
{"char": 247, "minX": 297, "minY": 300, "maxX": 346, "maxY": 400, "bold": true},
{"char": 254, "minX": 346, "minY": 300, "maxX": 395, "maxY": 400, "bold": true},
{"char": 242, "minX": 395, "minY": 300, "maxX": 444, "maxY": 400, "bold": true},
{"char": 243, "minX": 444, "minY": 300, "maxX": 493, "maxY": 400, "bold": true},
{"char": 111, "minX": 493, "minY": 300, "maxX": 542, "maxY": 400, "bold": true},
{"char": 88, "minX": 542, "minY": 300, "maxX": 593, "maxY": 396, "bold": false},
{"char": 38, "minX": 593, "minY": 300, "maxX": 644, "maxY": 396, "bold": false},
{"char": 167, "minX": 644, "minY": 300, "maxX": 694, "maxY": 396, "bold": false},
{"char": 82, "minX": 694, "minY": 300, "maxX": 744, "maxY": 396, "bold": false},
{"char": 75, "minX": 744, "minY": 300, "maxX": 794, "maxY": 396, "bold": false},
{"char": 66, "minX": 794, "minY": 300, "maxX": 844, "maxY": 396, "bold": false},
{"char": 223, "minX": 844, "minY": 300, "maxX": 893, "maxY": 396, "bold": false},
{"char": 84, "minX": 893, "minY": 300, "maxX": 942, "maxY": 396, "bold": false},
{"char": 89, "minX": 942, "minY": 300, "maxX": 991, "maxY": 396, "bold": false},
{"char": 35, "minX": 991, "minY": 300, "maxX": 1040, "maxY": 396, "bold": false},
{"char": 222, "minX": 1040, "minY": 300, "maxX": 1088, "maxY": 396, "bold": false},
{"char": 83, "minX": 1088, "minY": 300, "maxX": 1136, "maxY": 396, "bold": false},
{"char": 90, "minX": 1136, "minY": 300, "maxX": 1184, "maxY": 396, "bold": false},
{"char": 240, "minX": 1184, "minY": 300, "maxX": 1231, "maxY": 396, "bold": false},
{"char": 181, "minX": 1231, "minY": 300, "maxX": 1278, "maxY": 396, "bold": false},
{"char": 163, "minX": 1278, "minY": 300, "maxX": 1325, "maxY": 396, "bold": false},
{"char": 244, "minX": 1325, "minY": 300, "maxX": 1371, "maxY": 396, "bold": false},
{"char": 245, "minX": 1371, "minY": 300, "maxX": 1417, "maxY": 396, "bold": false},
{"char": 246, "minX": 1417, "minY": 300, "maxX": 1463, "maxY": 396, "bold": false},
{"char": 247, "minX": 1463, "minY": 300, "maxX": 1509, "maxY": 396, "bold": false},
{"char": 254, "minX": 1509, "minY": 300, "maxX": 1555, "maxY": 396, "bold": false},
{"char": 242, "minX": 1555, "minY": 300, "maxX": 1601, "maxY": 396, "bold": false},
{"char": 243, "minX": 1601, "minY": 300, "maxX": 1647, "maxY": 396, "bold": false},
{"char": 111, "minX": 1647, "minY": 300, "maxX": 1693, "maxY": 396, "bold": false},
{"char": 248, "minX": 1693, "minY": 300, "maxX": 1738, "maxY": 396, "bold": false},
{"char": 241, "minX": 1738, "minY": 300, "maxX": 1783, "maxY": 396, "bold": false},
{"char": 200, "minX": 1783, "minY": 300, "maxX": 1828, "maxY": 396, "bold": false},
{"char": 201, "minX": 1828, "minY": 300, "maxX": 1873, "maxY": 396, "bold": false},
{"char": 202, "minX": 1873, "minY": 300, "maxX": 1918, "maxY": 396, "bold": false},
{"char": 203, "minX": 1918, "minY": 300, "maxX": 1963, "maxY": 396, "bold": false},
{"char": 162, "minX": 1963, "minY": 300, "maxX": 2008, "maxY": 396, "bold": false},
{"char": 118, "minX": 2008, "minY": 300, "maxX": 2048, "maxY": 396, "bold": false},
{"char": 248, "minX": 0, "minY": 400, "maxX": 48, "maxY": 500, "bold": true},
{"char": 241, "minX": 48, "minY": 400, "maxX": 96, "maxY": 500, "bold": true},
{"char": 200, "minX": 96, "minY": 400, "maxX": 144, "maxY": 500, "bold": true},
{"char": 201, "minX": 144, "minY": 400, "maxX": 192, "maxY": 500, "bold": true},
{"char": 202, "minX": 192, "minY": 400, "maxX": 240, "maxY": 500, "bold": true},
{"char": 203, "minX": 240, "minY": 400, "maxX": 288, "maxY": 500, "bold": true},
{"char": 162, "minX": 288, "minY": 400, "maxX": 336, "maxY": 500, "bold": true},
{"char": 165, "minX": 336, "minY": 400, "maxX": 384, "maxY": 500, "bold": true},
{"char": 103, "minX": 384, "minY": 400, "maxX": 432, "maxY": 500, "bold": true},
{"char": 110, "minX": 432, "minY": 400, "maxX": 480, "maxY": 500, "bold": true},
{"char": 112, "minX": 480, "minY": 400, "maxX": 528, "maxY": 500, "bold": true},
{"char": 113, "minX": 528, "minY": 400, "maxX": 576, "maxY": 500, "bold": true},
{"char": 98, "minX": 576, "minY": 400, "maxX": 624, "maxY": 500, "bold": true},
{"char": 100, "minX": 624, "minY": 400, "maxX": 672, "maxY": 500, "bold": true},
{"char": 69, "minX": 672, "minY": 400, "maxX": 720, "maxY": 500, "bold": true},
{"char": 55, "minX": 720, "minY": 400, "maxX": 768, "maxY": 500, "bold": true},
{"char": 56, "minX": 768, "minY": 400, "maxX": 816, "maxY": 500, "bold": true},
{"char": 57, "minX": 816, "minY": 400, "maxX": 864, "maxY": 500, "bold": true},
{"char": 61, "minX": 864, "minY": 400, "maxX": 912, "maxY": 500, "bold": true},
{"char": 43, "minX": 912, "minY": 400, "maxX": 960, "maxY": 500, "bold": true},
{"char": 48, "minX": 960, "minY": 400, "maxX": 1008, "maxY": 500, "bold": true},
{"char": 49, "minX": 1008, "minY": 400, "maxX": 1056, "maxY": 500, "bold": true},
{"char": 50, "minX": 1056, "minY": 400, "maxX": 1104, "maxY": 500, "bold": true},
{"char": 51, "minX": 1104, "minY": 400, "maxX": 1152, "maxY": 500, "bold": true},
{"char": 52, "minX": 1152, "minY": 400, "maxX": 1200, "maxY": 500, "bold": true},
{"char": 53, "minX": 1200, "minY": 400, "maxX": 1248, "maxY": 500, "bold": true},
{"char": 54, "minX": 1248, "minY": 400, "maxX": 1296, "maxY": 500, "bold": true},
{"char": 36, "minX": 1296, "minY": 400, "maxX": 1344, "maxY": 500, "bold": true},
{"char": 249, "minX": 1344, "minY": 400, "maxX": 1391, "maxY": 500, "bold": true},
{"char": 250, "minX": 1391, "minY": 400, "maxX": 1438, "maxY": 500, "bold": true},
{"char": 251, "minX": 1438, "minY": 400, "maxX": 1485, "maxY": 500, "bold": true},
{"char": 252, "minX": 1485, "minY": 400, "maxX": 1532, "maxY": 500, "bold": true},
{"char": 172, "minX": 1532, "minY": 400, "maxX": 1579, "maxY": 500, "bold": true},
{"char": 117, "minX": 1579, "minY": 400, "maxX": 1626, "maxY": 500, "bold": true},
{"char": 104, "minX": 1626, "minY": 400, "maxX": 1673, "maxY": 500, "bold": true},
{"char": 70, "minX": 1673, "minY": 400, "maxX": 1720, "maxY": 500, "bold": true},
{"char": 74, "minX": 1720, "minY": 400, "maxX": 1767, "maxY": 500, "bold": true},
{"char": 232, "minX": 1767, "minY": 400, "maxX": 1813, "maxY": 500, "bold": true},
{"char": 233, "minX": 1813, "minY": 400, "maxX": 1859, "maxY": 500, "bold": true},
{"char": 234, "minX": 1859, "minY": 400, "maxX": 1905, "maxY": 500, "bold": true},
{"char": 235, "minX": 1905, "minY": 400, "maxX": 1951, "maxY": 500, "bold": true},
{"char": 224, "minX": 1951, "minY": 400, "maxX": 1997, "maxY": 500, "bold": true},
{"char": 225, "minX": 1997, "minY": 400, "maxX": 2043, "maxY": 500, "bold": true},
{"char": 226, "minX": 0, "minY": 500, "maxX": 46, "maxY": 600, "bold": true},
{"char": 227, "minX": 46, "minY": 500, "maxX": 92, "maxY": 600, "bold": true},
{"char": 228, "minX": 92, "minY": 500, "maxX": 138, "maxY": 600, "bold": true},
{"char": 229, "minX": 138, "minY": 500, "maxX": 184, "maxY": 600, "bold": true},
{"char": 215, "minX": 184, "minY": 500, "maxX": 230, "maxY": 600, "bold": true},
{"char": 177, "minX": 230, "minY": 500, "maxX": 276, "maxY": 600, "bold": true},
{"char": 97, "minX": 276, "minY": 500, "maxX": 322, "maxY": 600, "bold": true},
{"char": 101, "minX": 322, "minY": 500, "maxX": 368, "maxY": 600, "bold": true},
{"char": 76, "minX": 368, "minY": 500, "maxX": 414, "maxY": 600, "bold": true},
{"char": 231, "minX": 414, "minY": 500, "maxX": 459, "maxY": 600, "bold": true},
{"char": 120, "minX": 459, "minY": 500, "maxX": 504, "maxY": 600, "bold": true},
{"char": 107, "minX": 504, "minY": 500, "maxX": 549, "maxY": 600, "bold": true},
{"char": 99, "minX": 549, "minY": 500, "maxX": 594, "maxY": 600, "bold": true},
{"char": 62, "minX": 594, "minY": 500, "maxX": 639, "maxY": 600, "bold": true},
{"char": 253, "minX": 639, "minY": 500, "maxX": 683, "maxY": 600, "bold": true},
{"char": 255, "minX": 683, "minY": 500, "maxX": 727, "maxY": 600, "bold": true},
{"char": 115, "minX": 727, "minY": 500, "maxX": 771, "maxY": 600, "bold": true},
{"char": 121, "minX": 771, "minY": 500, "maxX": 815, "maxY": 600, "bold": true},
{"char": 60, "minX": 815, "minY": 500, "maxX": 859, "maxY": 600, "bold": true},
{"char": 103, "minX": 859, "minY": 500, "maxX": 904, "maxY": 596, "bold": false},
{"char": 110, "minX": 904, "minY": 500, "maxX": 949, "maxY": 596, "bold": false},
{"char": 112, "minX": 949, "minY": 500, "maxX": 994, "maxY": 596, "bold": false},
{"char": 113, "minX": 994, "minY": 500, "maxX": 1039, "maxY": 596, "bold": false},
{"char": 98, "minX": 1039, "minY": 500, "maxX": 1084, "maxY": 596, "bold": false},
{"char": 100, "minX": 1084, "minY": 500, "maxX": 1129, "maxY": 596, "bold": false},
{"char": 69, "minX": 1129, "minY": 500, "maxX": 1174, "maxY": 596, "bold": false},
{"char": 55, "minX": 1174, "minY": 500, "maxX": 1219, "maxY": 596, "bold": false},
{"char": 56, "minX": 1219, "minY": 500, "maxX": 1264, "maxY": 596, "bold": false},
{"char": 57, "minX": 1264, "minY": 500, "maxX": 1309, "maxY": 596, "bold": false},
{"char": 61, "minX": 1309, "minY": 500, "maxX": 1354, "maxY": 596, "bold": false},
{"char": 43, "minX": 1354, "minY": 500, "maxX": 1399, "maxY": 596, "bold": false},
{"char": 48, "minX": 1399, "minY": 500, "maxX": 1444, "maxY": 596, "bold": false},
{"char": 49, "minX": 1444, "minY": 500, "maxX": 1489, "maxY": 596, "bold": false},
{"char": 50, "minX": 1489, "minY": 500, "maxX": 1534, "maxY": 596, "bold": false},
{"char": 51, "minX": 1534, "minY": 500, "maxX": 1579, "maxY": 596, "bold": false},
{"char": 52, "minX": 1579, "minY": 500, "maxX": 1624, "maxY": 596, "bold": false},
{"char": 53, "minX": 1624, "minY": 500, "maxX": 1669, "maxY": 596, "bold": false},
{"char": 54, "minX": 1669, "minY": 500, "maxX": 1714, "maxY": 596, "bold": false},
{"char": 36, "minX": 1714, "minY": 500, "maxX": 1759, "maxY": 596, "bold": false},
{"char": 249, "minX": 1759, "minY": 500, "maxX": 1803, "maxY": 596, "bold": false},
{"char": 250, "minX": 1803, "minY": 500, "maxX": 1847, "maxY": 596, "bold": false},
{"char": 251, "minX": 1847, "minY": 500, "maxX": 1891, "maxY": 596, "bold": false},
{"char": 252, "minX": 1891, "minY": 500, "maxX": 1935, "maxY": 596, "bold": false},
{"char": 172, "minX": 1935, "minY": 500, "maxX": 1979, "maxY": 596, "bold": false},
{"char": 117, "minX": 1979, "minY": 500, "maxX": 2023, "maxY": 596, "bold": false},
{"char": 205, "minX": 2023, "minY": 500, "maxX": 2046, "maxY": 596, "bold": false},
{"char": 118, "minX": 0, "minY": 600, "maxX": 43, "maxY": 700, "bold": true},
{"char": 122, "minX": 43, "minY": 600, "maxX": 86, "maxY": 700, "bold": true},
{"char": 104, "minX": 86, "minY": 600, "maxX": 130, "maxY": 696, "bold": false},
{"char": 70, "minX": 130, "minY": 600, "maxX": 174, "maxY": 696, "bold": false},
{"char": 74, "minX": 174, "minY": 600, "maxX": 218, "maxY": 696, "bold": false},
{"char": 232, "minX": 218, "minY": 600, "maxX": 261, "maxY": 696, "bold": false},
{"char": 233, "minX": 261, "minY": 600, "maxX": 304, "maxY": 696, "bold": false},
{"char": 234, "minX": 304, "minY": 600, "maxX": 347, "maxY": 696, "bold": false},
{"char": 235, "minX": 347, "minY": 600, "maxX": 390, "maxY": 696, "bold": false},
{"char": 224, "minX": 390, "minY": 600, "maxX": 433, "maxY": 696, "bold": false},
{"char": 225, "minX": 433, "minY": 600, "maxX": 476, "maxY": 696, "bold": false},
{"char": 226, "minX": 476, "minY": 600, "maxX": 519, "maxY": 696, "bold": false},
{"char": 227, "minX": 519, "minY": 600, "maxX": 562, "maxY": 696, "bold": false},
{"char": 228, "minX": 562, "minY": 600, "maxX": 605, "maxY": 696, "bold": false},
{"char": 229, "minX": 605, "minY": 600, "maxX": 648, "maxY": 696, "bold": false},
{"char": 215, "minX": 648, "minY": 600, "maxX": 691, "maxY": 696, "bold": false},
{"char": 177, "minX": 691, "minY": 600, "maxX": 734, "maxY": 696, "bold": false},
{"char": 165, "minX": 734, "minY": 600, "maxX": 777, "maxY": 696, "bold": false},
{"char": 97, "minX": 777, "minY": 600, "maxX": 820, "maxY": 696, "bold": false},
{"char": 101, "minX": 820, "minY": 600, "maxX": 863, "maxY": 696, "bold": false},
{"char": 76, "minX": 863, "minY": 600, "maxX": 906, "maxY": 696, "bold": false},
{"char": 107, "minX": 906, "minY": 600, "maxX": 948, "maxY": 696, "bold": false},
{"char": 99, "minX": 948, "minY": 600, "maxX": 990, "maxY": 696, "bold": false},
{"char": 62, "minX": 990, "minY": 600, "maxX": 1032, "maxY": 696, "bold": false},
{"char": 115, "minX": 1032, "minY": 600, "maxX": 1073, "maxY": 696, "bold": false},
{"char": 60, "minX": 1073, "minY": 600, "maxX": 1114, "maxY": 696, "bold": false},
{"char": 120, "minX": 1114, "minY": 600, "maxX": 1154, "maxY": 696, "bold": false},
{"char": 122, "minX": 1154, "minY": 600, "maxX": 1194, "maxY": 696, "bold": false},
{"char": 253, "minX": 1194, "minY": 600, "maxX": 1233, "maxY": 696, "bold": false},
{"char": 255, "minX": 1233, "minY": 600, "maxX": 1272, "maxY": 696, "bold": false},
{"char": 187, "minX": 1272, "minY": 600, "maxX": 1311, "maxY": 696, "bold": false},
{"char": 191, "minX": 1311, "minY": 600, "maxX": 1350, "maxY": 696, "bold": false},
{"char": 175, "minX": 1350, "minY": 600, "maxX": 1389, "maxY": 696, "bold": false},
{"char": 182, "minX": 1389, "minY": 600, "maxX": 1428, "maxY": 696, "bold": false},
{"char": 171, "minX": 1428, "minY": 600, "maxX": 1467, "maxY": 696, "bold": false},
{"char": 121, "minX": 1467, "minY": 600, "maxX": 1506, "maxY": 696, "bold": false},
{"char": 63, "minX": 1506, "minY": 600, "maxX": 1545, "maxY": 696, "bold": false},
{"char": 186, "minX": 1545, "minY": 600, "maxX": 1582, "maxY": 696, "bold": false},
{"char": 170, "minX": 1582, "minY": 600, "maxX": 1618, "maxY": 696, "bold": false},
{"char": 95, "minX": 1618, "minY": 600, "maxX": 1654, "maxY": 696, "bold": false},
{"char": 168, "minX": 1654, "minY": 600, "maxX": 1689, "maxY": 696, "bold": false},
{"char": 42, "minX": 1689, "minY": 600, "maxX": 1724, "maxY": 696, "bold": false},
{"char": 94, "minX": 1724, "minY": 600, "maxX": 1758, "maxY": 696, "bold": false},
{"char": 92, "minX": 1758, "minY": 600, "maxX": 1791, "maxY": 696, "bold": false},
{"char": 47, "minX": 1791, "minY": 600, "maxX": 1823, "maxY": 696, "bold": false},
{"char": 185, "minX": 1823, "minY": 600, "maxX": 1853, "maxY": 696, "bold": false},
{"char": 176, "minX": 1853, "minY": 600, "maxX": 1883, "maxY": 696, "bold": false},
{"char": 178, "minX": 1883, "minY": 600, "maxX": 1913, "maxY": 696, "bold": false},
{"char": 179, "minX": 1913, "minY": 600, "maxX": 1943, "maxY": 696, "bold": false},
{"char": 239, "minX": 1943, "minY": 600, "maxX": 1972, "maxY": 696, "bold": false},
{"char": 207, "minX": 1972, "minY": 600, "maxX": 2001, "maxY": 696, "bold": false},
{"char": 238, "minX": 2001, "minY": 600, "maxX": 2029, "maxY": 696, "bold": false},
{"char": 106, "minX": 2029, "minY": 600, "maxX": 2048, "maxY": 696, "bold": false},
{"char": 187, "minX": 0, "minY": 700, "maxX": 42, "maxY": 800, "bold": true},
{"char": 191, "minX": 42, "minY": 700, "maxX": 84, "maxY": 800, "bold": true},
{"char": 175, "minX": 84, "minY": 700, "maxX": 126, "maxY": 800, "bold": true},
{"char": 182, "minX": 126, "minY": 700, "maxX": 168, "maxY": 800, "bold": true},
{"char": 171, "minX": 168, "minY": 700, "maxX": 210, "maxY": 800, "bold": true},
{"char": 63, "minX": 210, "minY": 700, "maxX": 252, "maxY": 800, "bold": true},
{"char": 186, "minX": 252, "minY": 700, "maxX": 292, "maxY": 800, "bold": true},
{"char": 170, "minX": 292, "minY": 700, "maxX": 331, "maxY": 800, "bold": true},
{"char": 95, "minX": 331, "minY": 700, "maxX": 370, "maxY": 800, "bold": true},
{"char": 168, "minX": 370, "minY": 700, "maxX": 408, "maxY": 800, "bold": true},
{"char": 42, "minX": 408, "minY": 700, "maxX": 446, "maxY": 800, "bold": true},
{"char": 94, "minX": 446, "minY": 700, "maxX": 483, "maxY": 800, "bold": true},
{"char": 92, "minX": 483, "minY": 700, "maxX": 519, "maxY": 800, "bold": true},
{"char": 47, "minX": 519, "minY": 700, "maxX": 554, "maxY": 800, "bold": true},
{"char": 185, "minX": 554, "minY": 700, "maxX": 587, "maxY": 800, "bold": true},
{"char": 176, "minX": 587, "minY": 700, "maxX": 620, "maxY": 800, "bold": true},
{"char": 178, "minX": 620, "minY": 700, "maxX": 653, "maxY": 800, "bold": true},
{"char": 179, "minX": 653, "minY": 700, "maxX": 686, "maxY": 800, "bold": true},
{"char": 207, "minX": 686, "minY": 700, "maxX": 718, "maxY": 800, "bold": true},
{"char": 238, "minX": 718, "minY": 700, "maxX": 749, "maxY": 800, "bold": true},
{"char": 206, "minX": 749, "minY": 700, "maxX": 780, "maxY": 800, "bold": true},
{"char": 114, "minX": 780, "minY": 700, "maxX": 811, "maxY": 800, "bold": true},
{"char": 102, "minX": 811, "minY": 700, "maxX": 842, "maxY": 800, "bold": true},
{"char": 40, "minX": 842, "minY": 700, "maxX": 873, "maxY": 800, "bold": true},
{"char": 41, "minX": 873, "minY": 700, "maxX": 904, "maxY": 800, "bold": true},
{"char": 116, "minX": 904, "minY": 700, "maxX": 934, "maxY": 800, "bold": true},
{"char": 123, "minX": 934, "minY": 700, "maxX": 964, "maxY": 800, "bold": true},
{"char": 125, "minX": 964, "minY": 700, "maxX": 994, "maxY": 800, "bold": true},
{"char": 173, "minX": 994, "minY": 700, "maxX": 1023, "maxY": 800, "bold": true},
{"char": 180, "minX": 1023, "minY": 700, "maxX": 1052, "maxY": 800, "bold": true},
{"char": 45, "minX": 1052, "minY": 700, "maxX": 1081, "maxY": 800, "bold": true},
{"char": 34, "minX": 1081, "minY": 700, "maxX": 1110, "maxY": 800, "bold": true},
{"char": 206, "minX": 1110, "minY": 700, "maxX": 1138, "maxY": 796, "bold": false},
{"char": 114, "minX": 1138, "minY": 700, "maxX": 1166, "maxY": 796, "bold": false},
{"char": 102, "minX": 1166, "minY": 700, "maxX": 1194, "maxY": 796, "bold": false},
{"char": 40, "minX": 1194, "minY": 700, "maxX": 1222, "maxY": 796, "bold": false},
{"char": 41, "minX": 1222, "minY": 700, "maxX": 1250, "maxY": 796, "bold": false},
{"char": 116, "minX": 1250, "minY": 700, "maxX": 1277, "maxY": 796, "bold": false},
{"char": 123, "minX": 1277, "minY": 700, "maxX": 1304, "maxY": 796, "bold": false},
{"char": 125, "minX": 1304, "minY": 700, "maxX": 1331, "maxY": 796, "bold": false},
{"char": 173, "minX": 1331, "minY": 700, "maxX": 1357, "maxY": 796, "bold": false},
{"char": 180, "minX": 1357, "minY": 700, "maxX": 1383, "maxY": 796, "bold": false},
{"char": 96, "minX": 1383, "minY": 700, "maxX": 1409, "maxY": 796, "bold": false},
{"char": 45, "minX": 1409, "minY": 700, "maxX": 1435, "maxY": 796, "bold": false},
{"char": 34, "minX": 1435, "minY": 700, "maxX": 1461, "maxY": 796, "bold": false},
{"char": 183, "minX": 1461, "minY": 700, "maxX": 1484, "maxY": 796, "bold": false},
{"char": 73, "minX": 1484, "minY": 700, "maxX": 1507, "maxY": 796, "bold": false},
{"char": 91, "minX": 1507, "minY": 700, "maxX": 1529, "maxY": 796, "bold": false},
{"char": 93, "minX": 1529, "minY": 700, "maxX": 1551, "maxY": 796, "bold": false},
{"char": 46, "minX": 1551, "minY": 700, "maxX": 1573, "maxY": 796, "bold": false},
{"char": 237, "minX": 1573, "minY": 700, "maxX": 1594, "maxY": 796, "bold": false},
{"char": 184, "minX": 1594, "minY": 700, "maxX": 1615, "maxY": 796, "bold": false},
{"char": 161, "minX": 1615, "minY": 700, "maxX": 1636, "maxY": 796, "bold": false},
{"char": 58, "minX": 1636, "minY": 700, "maxX": 1657, "maxY": 796, "bold": false},
{"char": 33, "minX": 1657, "minY": 700, "maxX": 1678, "maxY": 796, "bold": false},
{"char": 236, "minX": 1678, "minY": 700, "maxX": 1698, "maxY": 796, "bold": false},
{"char": 204, "minX": 1698, "minY": 700, "maxX": 1718, "maxY": 796, "bold": false},
{"char": 160, "minX": 1718, "minY": 700, "maxX": 1738, "maxY": 796, "bold": false},
{"char": 166, "minX": 1738, "minY": 700, "maxX": 1758, "maxY": 796, "bold": false},
{"char": 124, "minX": 1758, "minY": 700, "maxX": 1778, "maxY": 796, "bold": false},
{"char": 105, "minX": 1778, "minY": 700, "maxX": 1798, "maxY": 796, "bold": false},
{"char": 108, "minX": 1798, "minY": 700, "maxX": 1818, "maxY": 796, "bold": false},
{"char": 32, "minX": 1818, "minY": 700, "maxX": 1838, "maxY": 796, "bold": false},
{"char": 59, "minX": 1838, "minY": 700, "maxX": 1857, "maxY": 796, "bold": false},
{"char": 44, "minX": 1857, "minY": 700, "maxX": 1875, "maxY": 796, "bold": false},
{"char": 39, "minX": 1875, "minY": 700, "maxX": 1889, "maxY": 796, "bold": false},
{"char": 236, "minX": 0, "minY": 800, "maxX": 26, "maxY": 900, "bold": true},
{"char": 204, "minX": 26, "minY": 800, "maxX": 52, "maxY": 900, "bold": true},
{"char": 205, "minX": 52, "minY": 800, "maxX": 78, "maxY": 900, "bold": true},
{"char": 183, "minX": 78, "minY": 800, "maxX": 104, "maxY": 900, "bold": true},
{"char": 96, "minX": 104, "minY": 800, "maxX": 130, "maxY": 900, "bold": true},
{"char": 73, "minX": 130, "minY": 800, "maxX": 156, "maxY": 900, "bold": true},
{"char": 91, "minX": 156, "minY": 800, "maxX": 181, "maxY": 900, "bold": true},
{"char": 93, "minX": 181, "minY": 800, "maxX": 206, "maxY": 900, "bold": true},
{"char": 46, "minX": 206, "minY": 800, "maxX": 231, "maxY": 900, "bold": true},
{"char": 237, "minX": 231, "minY": 800, "maxX": 255, "maxY": 900, "bold": true},
{"char": 184, "minX": 255, "minY": 800, "maxX": 279, "maxY": 900, "bold": true},
{"char": 161, "minX": 279, "minY": 800, "maxX": 303, "maxY": 900, "bold": true},
{"char": 58, "minX": 303, "minY": 800, "maxX": 327, "maxY": 900, "bold": true},
{"char": 33, "minX": 327, "minY": 800, "maxX": 351, "maxY": 900, "bold": true},
{"char": 160, "minX": 351, "minY": 800, "maxX": 374, "maxY": 900, "bold": true},
{"char": 166, "minX": 374, "minY": 800, "maxX": 397, "maxY": 900, "bold": true},
{"char": 124, "minX": 397, "minY": 800, "maxX": 420, "maxY": 900, "bold": true},
{"char": 105, "minX": 420, "minY": 800, "maxX": 443, "maxY": 900, "bold": true},
{"char": 108, "minX": 443, "minY": 800, "maxX": 466, "maxY": 900, "bold": true},
{"char": 32, "minX": 466, "minY": 800, "maxX": 489, "maxY": 900, "bold": true},
{"char": 106, "minX": 489, "minY": 800, "maxX": 511, "maxY": 900, "bold": true},
{"char": 59, "minX": 511, "minY": 800, "maxX": 533, "maxY": 900, "bold": true},
{"char": 44, "minX": 533, "minY": 800, "maxX": 554, "maxY": 900, "bold": true},
{"char": 39, "minX": 554, "minY": 800, "maxX": 571, "maxY": 900, "bold": true},
{"char": 0, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": false},
{"char": 2, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": false},
{"char": 9, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": false},
{"char": 10, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": false},
{"char": 13, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": false},
{"char": 0, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": true},
{"char": 2, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": true},
{"char": 9, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": true},
{"char": 10, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": true},
{"char": 13, "minX": 0, "minY": 0, "maxX": 0, "maxY": 0, "bold": true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB