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.ints.collections.IntIterator; import speiger.src.collections.ints.utils.IntIterators; 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 createBitmapFont(InputStream stream, float size) { return createBitmapFont(stream, size, "UTF-8"); } public static ObjectObjectPair createBitmapFont(InputStream stream, float size, String charset) { ObjectObjectPair, List> result = createBitmapFont(stream, charset, 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 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, List> createBitmapFont(InputStream ttf, String characters, int flags, float size) { try { Map toDraw = new Object2ObjectLinkedOpenHashMap<>(); Builder builder = TextureAtlas.create(); Consumer data = T -> { AssetLocation location = T.asLocation(); toDraw.put(location, T); if(T.width > 0 && !builder.add(location, T.width, T.getExtraY(T.height), 2)) 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, List> 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(new String(Character.toChars(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(int c = 0;c < 0x110000;c++) { if(!Character.isDefined(c)) continue; char[] chars = Character.toChars(c); if(encoder.canEncode(new String(chars)) && font.canDisplay(c)) builder.append(chars); } return builder.toString(); } private static void loadFontData(Font font, String chars, Consumer 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 data = new ObjectArrayList<>(); int c = 0; for(IntIterator iter = IntIterators.wrap(chars.codePoints().iterator());iter.hasNext();c++) { int next = iter.nextInt(); Rectangle rect = font.layoutGlyphVector(graphics.getFontRenderContext(), Character.toChars(next), 0, Character.charCount(next), 0).getGlyphPixelBounds(0, graphics.getFontRenderContext(), 0.0F, 0.0F); extraHeight = Math.min(extraHeight, rect.y); data.add(new CharData(font, next, rect, metric.charWidth(next), metric.getHeight(), font.isBold())); if(c % 1000 == 0) System.out.println("Rendered: "+c+" / "+chars.length()+" Chars"); } extraHeight = -(extraHeight+metric.getAscent()); for(int i = 0,m=data.size();i= 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 int getLetter() { return letter; } public AssetLocation asLocation() { return AssetLocation.of("base", font.getFontName().replaceAll(" ", "_") + (bold ? "_Bold" : "") + "_" + (letter)); } } }