refactoring FontEngine
This commit is contained in:
@@ -15,28 +15,34 @@ import java.io.InputStream;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ru.dmitriymx.lwjgl.tools.Tessellator;
|
|
||||||
import ru.dmitriymx.lwjgl.tools.Texture;
|
import ru.dmitriymx.lwjgl.tools.Texture;
|
||||||
import static org.lwjgl.opengl.GL11.*;
|
|
||||||
|
|
||||||
public class FontEngine {
|
public class FontEngine {
|
||||||
private Font font;
|
private Font font;
|
||||||
private int fontSize;
|
|
||||||
private Texture glyphTexture;
|
private Texture glyphTexture;
|
||||||
private int texture_width = 128, texture_height = 128;
|
private int texture_width = 128, texture_height = 128;
|
||||||
private Graphics2D g2d;
|
private Graphics2D g2d;
|
||||||
private boolean anti_aliasing = false;
|
private boolean anti_aliasing = false;
|
||||||
private float[] color4f = new float[]{1f, 1f, 1f, 1f};
|
private Map<Character, CharData> charMap = new HashMap<>();
|
||||||
// int[] = texture_x, // 0
|
private int texture_offset_char_x = 0, texture_offset_char_y = 0, max_height = 0;
|
||||||
// texture_y, // 1
|
|
||||||
// visual_width, // 2
|
public class CharData {
|
||||||
// visual_height, // 3
|
public final float texture_offset_x1, texture_offset_y1, texture_offset_x2, texture_offset_y2;
|
||||||
// visual_y, // 4
|
public final int visual_bounds_width, visual_bounds_height, visual_bounds_x, visual_bounds_y;
|
||||||
// visual_x, // 5
|
public final int width_char;
|
||||||
// widthChar // 6
|
|
||||||
private Map<Character, int[]> charMap = new HashMap<>();
|
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) {
|
||||||
private int offset_x = 0, offset_y = 0, max_height = 0;
|
this.texture_offset_x1 = glyphTexture.floatX(texture_offset_x);
|
||||||
private Tessellator tess = Tessellator.getInstance();
|
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 {
|
public static void importFont(InputStream inputStream) throws IOException, FontFormatException {
|
||||||
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(Font.createFont(Font.TRUETYPE_FONT, inputStream));
|
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(Font.createFont(Font.TRUETYPE_FONT, inputStream));
|
||||||
@@ -46,143 +52,132 @@ public class FontEngine {
|
|||||||
this(fontName, size, false);
|
this(fontName, size, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FontEngine(String fontName, int size, boolean antiAliasing) {
|
public FontEngine(String fontName, int size, boolean useAntiAliasing) {
|
||||||
this(new Font(fontName, Font.PLAIN, size), antiAliasing);
|
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) {
|
public FontEngine(Font font) {
|
||||||
this(font, false);
|
this(font, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FontEngine(Font font, boolean antiAliasing) {
|
public FontEngine(Font font, boolean useAntiAliasing) {
|
||||||
|
this(font, useAntiAliasing, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontEngine(Font font, boolean useAntiAliasing, int initGlyphTextureWidth) {
|
||||||
this.font = font;
|
this.font = font;
|
||||||
this.fontSize = font.getSize();
|
this.anti_aliasing = useAntiAliasing;
|
||||||
|
texture_width = texture_height = initGlyphTextureWidth;
|
||||||
|
|
||||||
glyphTexture = new Texture(new BufferedImage(texture_width, texture_height, BufferedImage.TYPE_INT_ARGB));
|
glyphTexture = new Texture(createBlankImage());
|
||||||
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) {
|
// public void initializeString(String string) {
|
||||||
char[] chars = string.toCharArray();
|
// 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) {
|
for (char chr : chars) {
|
||||||
if (!charMap.containsKey(chr)) {
|
if (!charMap.containsKey(chr)) {
|
||||||
cache_char(chr, true);
|
needUpdTexture = true;
|
||||||
}
|
putChar(chr);
|
||||||
}
|
|
||||||
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) {
|
if (needUpdTexture) glyphTexture.updateTexture();
|
||||||
return cache_char(chr, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] cache_char(char chr, boolean dontUpdateTexture) {
|
public FontMetrics getFontMetrics() {
|
||||||
int[] data = draw_char_in_texture(chr);
|
return g2d.getFontMetrics();
|
||||||
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) {
|
private BufferedImage createBlankImage() {
|
||||||
GlyphVector glyphVector = font.createGlyphVector(g2d.getFontRenderContext(), String.valueOf(chr));
|
BufferedImage image = new BufferedImage(texture_width, texture_height, BufferedImage.TYPE_INT_ARGB);
|
||||||
Rectangle visualBounds = glyphVector.getVisualBounds().getBounds();
|
g2d = image.createGraphics();
|
||||||
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) {
|
if (anti_aliasing) {
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
|
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
|
||||||
}
|
}
|
||||||
g2d.setColor(Color.WHITE);
|
g2d.setColor(Color.WHITE);
|
||||||
g2d.setFont(font);
|
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;
|
texture_offset_char_x = 0;
|
||||||
offset_x = 0;
|
texture_offset_char_y = 0;
|
||||||
max_height = 0;
|
max_height = 0;
|
||||||
|
|
||||||
// отрисовка символов по новой
|
// отрисовка символов по новой
|
||||||
for (char chr : charMap.keySet()) {
|
for (char chr : charMap.keySet()) {
|
||||||
cache_char(chr, true);
|
putChar(chr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// обновление текстуры
|
// обновление текстуры
|
||||||
glyphTexture.setImage(img);
|
glyphTexture.setImage(img);
|
||||||
glyphTexture.updateTexture();
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user