/*
* Copyright 2016 Nathan Howard
*
* This file is part of OpenGrave
*
* OpenGrave is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenGrave is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenGrave. If not, see <http://www.gnu.org/licenses/>.
*/
package com.opengrave.og.resources;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
import com.opengrave.og.MainThread;
import com.opengrave.og.Util;
public class Font {
public Texture texture;
Integer twidth;
Integer theight;
Integer chars;
public Integer fontheight;
public HashMap<Integer, FontData> fontData = new HashMap<Integer, FontData>();
public Font() {
fontheight = 0;
}
public Font(java.awt.Font font, boolean aA) {
int yoffset = 0, xoffset = 0, count = 0, heightrow = 0, longestline = 0;
int texH = 0, texW = 0;
// Pass one, get sizes etc
fontheight = 0;
int charLimit = 4000; // TODO Theoretically we could do all Unicode in one image. Realistically, we need a better system of placing glyphs (no wasted space) and large texture support.
// For now, limit to basic latin unicode
int lineLimit = (int) (Math.sqrt(charLimit) * 1.3f); // Most chars are taller than wide, try to make up for it here without knowing
for (int i = 0; i < charLimit; i++) {
count++;
if (count >= lineLimit) {
count = 0;
xoffset = 0;
yoffset += heightrow;
fontheight = Math.max(heightrow, fontheight);
}
if (i == 127) {
continue;
}
char c = (char) i;
BufferedImage ch = createCharImage(font, c, aA);
if (ch == null) {
continue;
}
longestline = Math.max(longestline, xoffset + ch.getWidth());
heightrow = Math.max(ch.getHeight(), heightrow);
xoffset += ch.getWidth();
}
texH = yoffset + heightrow;
texW = longestline;
BufferedImage image = new BufferedImage(texW, texH, BufferedImage.TYPE_INT_ARGB);
Graphics2D grap = image.createGraphics();
grap.setBackground(new Color(1f, 1f, 1f, 0f));
grap.clearRect(0, 0, texW, texH);
xoffset = 0;
yoffset = 0;
System.out.println("Font texture sized " + texW + " " + texH);
// TextureEditable ted = new TextureEditable(Math.max(texH, texW), 1f, 1f, 1f, 0f);
// Pass two, create a texture
for (int i = 0; i < charLimit; i++) {
count++;
if (count >= lineLimit) {
count = 0;
xoffset = 0;
yoffset += heightrow + 1;
}
if (i == 127) {
continue;
}
char c = (char) i;
BufferedImage ch = createCharImage(font, c, aA);
if (ch == null) {
continue;
}
FontData fd = new FontData();
fd.x = xoffset;
fd.y = yoffset;
fd.x2 = xoffset + ch.getWidth();
fd.y2 = yoffset + ch.getHeight();
fd.width = ch.getWidth();
fd.height = ch.getHeight();
fontData.put(i, fd);
grap.drawImage(ch, xoffset, yoffset, null);
xoffset += ch.getWidth() + 1;
}
System.out.println("Font image created");
int[] pixels = new int[texW * texH];
image.getRGB(0, 0, texW, texH, pixels, 0, texW);
TextureAtlasEditable ted = new TextureAtlasEditable(Math.max(texH, texW), pixels, texW, texH);
System.out.println("Created texture for font sized " + ted.size);
texture = ted;
int max = ted.size;
for (FontData fd : fontData.values()) {
fd.x = fd.x / max;
fd.y = fd.y / max;
fd.x2 = fd.x2 / max;
fd.y2 = fd.y2 / max;
}
}
public BufferedImage createCharImage(java.awt.Font font, char c, boolean aA) {
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
if (aA) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
FontMetrics metrics = g.getFontMetrics();
g.dispose();
int charWidth = metrics.charWidth(c);
int charHeight = metrics.getHeight();
if (charWidth <= 0) {
return null;
}
image = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB);
g = image.createGraphics();
g.setBackground(new Color(1f, 1f, 1f, 0f));
g.clearRect(0, 0, charWidth, charHeight);
if (aA) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
g.setPaint(new Color(1f, 1f, 1f, 1f));
g.drawString(String.valueOf(c), 0, metrics.getAscent());
g.dispose();
return image;
}
public boolean loadFont(String name) {
boolean passed = true;
BufferedReader br;
File f = new File(MainThread.cache, name);
if (!f.exists()) {
System.out.println("Cannot open file " + f.getAbsolutePath());
return false;
}
FileReader fr;
try {
fr = new FileReader(f.getAbsolutePath());
} catch (FileNotFoundException e) {
System.out.println("Cannot open file " + f.getAbsolutePath());
return false;
}
br = new BufferedReader(fr);
try (Scanner scan = new Scanner(br)) {
String sCurrentLine;
sCurrentLine = scan.next();
while (sCurrentLine != null) {
if (sCurrentLine.equals("page")) {
scan.next();
String raw = scan.next();
ArrayList<String> texNames = new ArrayList<String>();
texNames.add("tex/" + raw.substring(6, raw.length() - 1));
texture = Resources.loadTextures(texNames);
Util.checkErr();
scan.findWithinHorizon("\n", 0);
} else if (sCurrentLine.equals("common")) {
fontheight = getNumber(scan.next());
scan.next();
twidth = getNumber(scan.next());
theight = getNumber(scan.next());
scan.findWithinHorizon("\n", 0);
} else if (sCurrentLine.equals("chars")) {
chars = getNumber(scan.next());
scan.findWithinHorizon("\n", 0);
} else if (sCurrentLine.equals("char")) {
FontData fd = new FontData();
int chara = getNumber(scan.next());
fd.character = chara;
fd.x = getNumber(scan.next()) / (twidth * 1f);
fd.y = getNumber(scan.next()) / (theight * 1f);
fd.width = getNumber(scan.next());
fd.height = getNumber(scan.next());
fd.x2 = fd.x + fd.width / (twidth * 1f);
fd.y2 = fd.y + fd.height / (theight * 1f);
scan.findWithinHorizon("\n", 0);
fontData.put(chara, fd);
}
sCurrentLine = scan.next();
}
} catch (java.util.NoSuchElementException e) {
} finally {
try {
fr.close();
} catch (IOException e) {
}
}
return passed;
}
private Integer getNumber(String s) {
s = s.replaceAll("[^0-9]", "");
return Integer.parseInt(s);
}
}