diff --git a/src/java/ru/dmitriymx/lwjgl/tools/fontengine/FontEngine.java b/src/java/ru/dmitriymx/lwjgl/tools/fontengine/FontEngine.java index e6bf724..281bf48 100644 --- a/src/java/ru/dmitriymx/lwjgl/tools/fontengine/FontEngine.java +++ b/src/java/ru/dmitriymx/lwjgl/tools/fontengine/FontEngine.java @@ -15,28 +15,34 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import ru.dmitriymx.lwjgl.tools.Tessellator; import ru.dmitriymx.lwjgl.tools.Texture; -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(); + private Map charMap = new HashMap<>(); + private int texture_offset_char_x = 0, texture_offset_char_y = 0, max_height = 0; + + public class CharData { + public final float texture_offset_x1, texture_offset_y1, texture_offset_x2, texture_offset_y2; + public final int visual_bounds_width, visual_bounds_height, visual_bounds_x, visual_bounds_y; + public final int width_char; + + private CharData(int texture_offset_x, int texture_offset_y, int visual_bounds_width, int visual_bounds_height, int visual_bounds_x, int visual_bounds_y, int width_char) { + this.texture_offset_x1 = glyphTexture.floatX(texture_offset_x); + this.texture_offset_y1 = glyphTexture.floatY(texture_offset_y); + this.texture_offset_x2 = glyphTexture.floatX(texture_offset_x + visual_bounds_width); + this.texture_offset_y2 = glyphTexture.floatY(texture_offset_y + visual_bounds_height); + this.visual_bounds_width = visual_bounds_width; + this.visual_bounds_height = visual_bounds_height; + this.visual_bounds_x = visual_bounds_x; + this.visual_bounds_y = visual_bounds_y; + this.width_char = width_char; + } + } public static void importFont(InputStream inputStream) throws IOException, FontFormatException { GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(Font.createFont(Font.TRUETYPE_FONT, inputStream)); @@ -44,145 +50,134 @@ public class FontEngine { 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(String fontName, int size, boolean useAntiAliasing) { + this(fontName, size, useAntiAliasing, 64); + } + + public FontEngine(String fontName, int size, boolean useAntiAliasing, int initGlyphTextureWidth) { + this(new Font(fontName, Font.PLAIN, size), useAntiAliasing, initGlyphTextureWidth); } 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 FontEngine(Font font, boolean useAntiAliasing) { + this(font, useAntiAliasing, 64); } - public void initializeString(String string) { - char[] chars = string.toCharArray(); + public FontEngine(Font font, boolean useAntiAliasing, int initGlyphTextureWidth) { + this.font = font; + this.anti_aliasing = useAntiAliasing; + texture_width = texture_height = initGlyphTextureWidth; + + glyphTexture = new Texture(createBlankImage()); + } + +// public void initializeString(String string) { +// char[] chars = string.toCharArray(); +// for (char chr : chars) { +// if (!charMap.containsKey(chr)) { +// cache_char(chr, true); +// } +// } +// glyphTexture.updateTexture(); +// } + + public Font getFont() { + return font; + } + + public void bindTexture() { + glyphTexture.bind(); + } + + public CharData getCharData(char chr) { + return charMap.get(chr); + } + + public void checkChars(char[] chars) { + boolean needUpdTexture = false; + for (char chr : chars) { if (!charMap.containsKey(chr)) { - cache_char(chr, true); + needUpdTexture = true; + putChar(chr); } } - glyphTexture.updateTexture(); + + if (needUpdTexture) glyphTexture.updateTexture(); } - public int getFontSize() { - return fontSize; + public FontMetrics getFontMetrics() { + return g2d.getFontMetrics(); } - 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(); + private BufferedImage createBlankImage() { + BufferedImage image = new BufferedImage(texture_width, texture_height, BufferedImage.TYPE_INT_ARGB); + g2d = image.createGraphics(); if (anti_aliasing) { g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); } g2d.setColor(Color.WHITE); g2d.setFont(font); - + + return image; + } + + // Добавляем символ в "базу": рисуем на текстуре и сохраняем метрические данные + private void putChar(char chr) { + CharData data = drawChar(chr); + texture_offset_char_x += data.visual_bounds_width + 1; + if (max_height < texture_offset_char_y + data.visual_bounds_height) { + max_height = texture_offset_char_y + data.visual_bounds_height; + } + charMap.put(chr, data); + } + + // Рисуем символ на текстуре. Возвращаются данные о символе + private CharData drawChar(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 (texture_offset_char_y >= texture_height || (texture_offset_char_y + visualBounds.height) > texture_height) { + recreateGlyphTexture(); + } + + if (texture_offset_char_x >= texture_width || (texture_offset_char_x + visualBounds.width) > texture_width) { + texture_offset_char_y = max_height + 1; + texture_offset_char_x = 0; + } + + g2d.drawGlyphVector(glyphVector, texture_offset_char_x - visualBounds.x, texture_offset_char_y - visualBounds.y); + return new CharData(texture_offset_char_x, texture_offset_char_y, visualBounds.width, visualBounds.height, ~visualBounds.x + 1, ~visualBounds.y + 1, widthChar); + } + + // Увеличиваем вместимость текстуры путем увеличения ее размеров. + private void recreateGlyphTexture() { + // удваение размеров текстуры + texture_width *= 2; + texture_height *= 2; + + // создание текстуры по новой + BufferedImage img = createBlankImage(); + // сброс "офсетов" - offset_y = 0; - offset_x = 0; + texture_offset_char_x = 0; + texture_offset_char_y = 0; max_height = 0; - + // отрисовка символов по новой for (char chr : charMap.keySet()) { - cache_char(chr, true); + putChar(chr); } // обновление текстуры 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(); - } }