From 786f90fa988485cf3d03a3ad3d7efbe65e29e897 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Fri, 18 Sep 2015 12:41:13 +0300 Subject: [PATCH] create FontEngine --- .../ru/dmitriymx/lwjgl/tools/FontEngine.java | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/java/ru/dmitriymx/lwjgl/tools/FontEngine.java diff --git a/src/java/ru/dmitriymx/lwjgl/tools/FontEngine.java b/src/java/ru/dmitriymx/lwjgl/tools/FontEngine.java new file mode 100644 index 0000000..31a7618 --- /dev/null +++ b/src/java/ru/dmitriymx/lwjgl/tools/FontEngine.java @@ -0,0 +1,186 @@ +package ru.dmitriymx.lwjgl.tools; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.font.GlyphVector; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.opengl.GL11.*; + +public class FontEngine { + private Font font; + private int fontSize; + private Texture glyphTexture; + private int texture_width = 128, texture_height = 128; + private Graphics2D g2d; + private boolean anti_aliasing = false; + private float[] color4f = new float[]{1f, 1f, 1f, 1f}; + // int[] = texture_x, // 0 + // texture_y, // 1 + // visual_width, // 2 + // visual_height, // 3 + // visual_y, // 4 + // visual_x, // 5 + // widthChar // 6 + private Map charMap = new HashMap<>(); + private int offset_x = 0, offset_y = 0, max_height = 0; + private Tessellator tess = Tessellator.getInstance(); + + public static void importFont(InputStream inputStream) throws IOException, FontFormatException { + GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(Font.createFont(Font.TRUETYPE_FONT, inputStream)); + } + + public FontEngine(String fontName, int size) { + this(fontName, size, false); + } + + public FontEngine(String fontName, int size, boolean antiAliasing) { + this(new Font(fontName, Font.PLAIN, size), antiAliasing); + } + + public FontEngine(Font font) { + this(font, false); + } + + public FontEngine(Font font, boolean antiAliasing) { + this.font = font; + this.fontSize = font.getSize(); + + glyphTexture = new Texture(new BufferedImage(texture_width, texture_height, BufferedImage.TYPE_INT_ARGB)); + g2d = glyphTexture.getImage().createGraphics(); + if (antiAliasing) { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); + anti_aliasing = true; + } + g2d.setColor(Color.WHITE); + g2d.setFont(font); + } + + public void initializeString(String string) { + char[] chars = string.toCharArray(); + for (char chr : chars) { + if (!charMap.containsKey(chr)) { + cache_char(chr, true); + } + } + glyphTexture.updateTexture(); + } + + public int getFontSize() { + return fontSize; + } + + public void setColor(float red, float green, float blue) { + setColor(red, green, blue, 1f); + } + + public void setColor(float red, float green, float blue, float alpha) { + this.color4f[0] = red; + this.color4f[1] = green; + this.color4f[2] = blue; + this.color4f[3] = alpha; + } + + public void drawString(String string) { + glyphTexture.bind(); + + char[] chars = string.toCharArray(); + int[] data; + float current_x = 0; + + for (char ch : chars) { + if (!charMap.containsKey(ch)) data = cache_char(ch); + else data = charMap.get(ch); + + current_x -= data[5]; + draw_char(data, current_x); + current_x += data[6]; + } + } + + private int[] cache_char(char chr) { + return cache_char(chr, false); + } + + private int[] cache_char(char chr, boolean dontUpdateTexture) { + int[] data = draw_char_in_texture(chr); + if (!dontUpdateTexture) glyphTexture.updateTexture(); + offset_x += data[2] + 1; + if (max_height < offset_y + data[3]) { + max_height = offset_y + data[3]; + } + charMap.put(chr, data); + return data; + } + + private int[] draw_char_in_texture(char chr) { + GlyphVector glyphVector = font.createGlyphVector(g2d.getFontRenderContext(), String.valueOf(chr)); + Rectangle visualBounds = glyphVector.getVisualBounds().getBounds(); + FontMetrics fontMetrics = g2d.getFontMetrics(); + int widthChar = fontMetrics.charWidth(chr) - visualBounds.x; + + if (offset_y >= texture_height || (offset_y + visualBounds.height) > texture_height) { + regen_texture(); + } + if (offset_x >= texture_width || (offset_x + visualBounds.width) > texture_width) { + offset_y = max_height + 1; + offset_x = 0; + } + + g2d.drawGlyphVector(glyphVector, offset_x - visualBounds.x, offset_y - visualBounds.y); + return new int[]{offset_x, offset_y, visualBounds.width, visualBounds.height, ~visualBounds.y + 1, ~visualBounds.x + 1, widthChar}; + } + + private void regen_texture() { + // удваение размеров текстуры + texture_width *= 2; + texture_height *= 2; + + // создание текстуры по новой + BufferedImage img = new BufferedImage(texture_width, texture_height, BufferedImage.TYPE_INT_ARGB); + g2d = img.createGraphics(); + if (anti_aliasing) { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); + } + g2d.setColor(Color.WHITE); + g2d.setFont(font); + + // сброс "офсетов" + offset_y = 0; + offset_x = 0; + max_height = 0; + + // отрисовка символов по новой + for (char chr : charMap.keySet()) { + cache_char(chr, true); + } + + // обновление текстуры + glyphTexture.setImage(img); + glyphTexture.updateTexture(); + } + + private void draw_char(int[] data, float x) { + float d0 = glyphTexture.floatX(data[0]); + float d1 = glyphTexture.floatX(data[1]); + float d02 = glyphTexture.floatX(data[0] + data[2]); + float d13 = glyphTexture.floatY(data[1] + data[3]); + + tess.startDrawingUseVA(GL_QUADS); + tess.setColor(1f,0f,0f).setTexture(d0, d1 ).addVertex(x, data[4], 0); + tess.setColor(0f,1f,0f).setTexture(d02, d1 ).addVertex(x + data[2], data[4], 0); + tess.setColor(0f,0f,1f).setTexture(d02, d13).addVertex(x + data[2], -data[3] + data[4], 0); + tess.setColor(1f,1f,0f).setTexture(d0, d13).addVertex(x, -data[3] + data[4], 0); + tess.drawVA(); + } +}