Font atlas is now compressed: empty characters are omitted.

This commit is contained in:
Andrea Cavalli 2017-12-09 23:00:04 +01:00
parent ca41857f0c
commit 6a99a0ea4c
10 changed files with 215 additions and 90 deletions

View File

@ -17,6 +17,7 @@ public class Main {
}
public Main(Screen screen, String[] args) {
System.out.println("WarpPI Calculator");
instance = this;
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Thread.currentThread().setName("Main thread");

View File

@ -0,0 +1,14 @@
package org.warp.picalculator;
public class TestJNI {
static {
// picalculatornative.dll on Windows or libpicalculatornative.so on Linux
System.loadLibrary("picalculatornative");
}
private native void sayHello();
public static void main(String[] args) {
// invoke the native method
new TestJNI().sayHello();
}
}

View File

@ -10,6 +10,7 @@ import java.io.InputStreamReader;
import java.io.StringWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
@ -737,4 +738,13 @@ public class Utils {
}
return mcm;
}
public static void gc() {
Object obj = new Object();
final WeakReference<Object> ref = new WeakReference<>(obj);
obj = null;
while (ref.get() != null) {
System.gc();
}
}
}

View File

@ -450,11 +450,11 @@ public class Keyboard {
{ /* ROW 0 */
{Key.SHIFT, Key.SHIFT, Key.SHIFT}, /* 0,0 */
{Key.ALPHA, Key.ALPHA, Key.ALPHA}, /* 0,1 */
{Key.BRIGHTNESS_CYCLE, Key.BRIGHTNESS_CYCLE_REVERSE, Key.NONE}, /* 0,2 */
{Key.NONE, Key.NONE, Key.NONE}, /* 0,2 */
{Key.NONE, Key.NONE, Key.NONE}, /* 0,3 */
{Key.NONE, Key.NONE, Key.NONE}, /* 0,4 */
{Key.NONE, Key.NONE, Key.NONE}, /* 0,5 */
{Key.NONE, Key.NONE, Key.NONE}, /* 0,6 */
{Key.BRIGHTNESS_CYCLE, Key.BRIGHTNESS_CYCLE_REVERSE, Key.NONE}, /* 0,6 */
{Key.SIMPLIFY, Key.STEP, Key.NONE} /* 0,7 */
},
{ /* ROW 1 */
@ -590,7 +590,7 @@ public class Keyboard {
refresh = true;
break;
case BRIGHTNESS_CYCLE_REVERSE:
if (StaticVars.debugOn) DisplayManager.INSTANCE.setScreen(new MarioScreen()); //TODO: rimuovere: prova
DisplayManager.INSTANCE.setScreen(new MarioScreen()); //TODO: rimuovere: prova
DisplayManager.INSTANCE.cycleBrightness(true);
refresh = true;
break;

View File

@ -35,61 +35,70 @@ public class CPUFont implements BinaryFont {
private final boolean isResource;
CPUFont(String fontName) throws IOException {
this(fontName, false);
}
CPUFont(String fontName, boolean onlyRaw) throws IOException {
isResource = true;
load("/font_" + fontName + ".rft");
load("/font_" + fontName + ".rft", onlyRaw);
}
CPUFont(String path, String fontName) throws IOException {
this(path, fontName, false);
}
CPUFont(String path, String fontName, boolean onlyRaw) throws IOException {
isResource = false;
load(path + "/font_" + fontName + ".rft");
load(path + "/font_" + fontName + ".rft", onlyRaw);
}
public static CPUFont loadTemporaryFont(String name) throws IOException {
return new CPUFont(name);
return new CPUFont(name, true);
}
public static CPUFont loadTemporaryFont(String path, String name) throws IOException {
return new CPUFont(path, name);
return new CPUFont(path, name, true);
}
@Override
public void load(String path) throws IOException {
load(path, false);
}
private void load(String path, boolean onlyRaw) throws IOException {
Utils.out.println(Utils.OUTPUTLEVEL_DEBUG_MIN, "Loading font " + path);
loadFont(path);
chars32 = new int[(intervalsTotalSize) * charIntCount];
for (int charCompressedIndex = 0; charCompressedIndex < intervalsTotalSize; charCompressedIndex++) {
final boolean[] currentChar = rawchars[charCompressedIndex];
if (currentChar == null) {
int currentInt = 0;
int currentBit = 0;
for (int i = 0; i < charS; i++) {
if (currentInt * intBits + currentBit >= (currentInt + 1) * intBits) {
currentInt += 1;
currentBit = 0;
if (!onlyRaw) {
chars32 = new int[(intervalsTotalSize) * charIntCount];
for (int charCompressedIndex = 0; charCompressedIndex < intervalsTotalSize; charCompressedIndex++) {
final boolean[] currentChar = rawchars[charCompressedIndex];
if (currentChar == null) {
int currentInt = 0;
int currentBit = 0;
for (int i = 0; i < charS; i++) {
if (currentInt * intBits + currentBit >= (currentInt + 1) * intBits) {
currentInt += 1;
currentBit = 0;
}
chars32[charCompressedIndex * charIntCount + currentInt] = (chars32[charCompressedIndex * charIntCount + currentInt] << 1) + 1;
currentBit += 1;
}
chars32[charCompressedIndex * charIntCount + currentInt] = (chars32[charCompressedIndex * charIntCount + currentInt] << 1) + 1;
currentBit += 1;
}
} else {
int currentInt = 0;
int currentBit = 0;
for (int i = 0; i < charS; i++) {
if (currentBit >= intBits) {
currentInt += 1;
currentBit = 0;
} else {
int currentInt = 0;
int currentBit = 0;
for (int i = 0; i < charS; i++) {
if (currentBit >= intBits) {
currentInt += 1;
currentBit = 0;
}
chars32[charCompressedIndex * charIntCount + currentInt] = (chars32[charCompressedIndex * charIntCount + currentInt]) | ((currentChar[i] ? 1 : 0) << currentBit);
currentBit++;
}
chars32[charCompressedIndex * charIntCount + currentInt] = (chars32[charCompressedIndex * charIntCount + currentInt]) | ((currentChar[i] ? 1 : 0) << currentBit);
currentBit++;
}
}
}
Object obj = new Object();
final WeakReference<Object> ref = new WeakReference<>(obj);
obj = null;
while (ref.get() != null) {
System.gc();
}
Utils.gc();
}
private void loadFont(String string) throws IOException {
@ -219,22 +228,45 @@ public class CPUFont implements BinaryFont {
final int[] indexes = new int[l];
final char[] chars = txt.toCharArray();
for (int i = 0; i < l; i++) {
int originalIndex = chars[i] & 0xFFFF;
int compressedIndex = 0;
for (Integer[] interval : intervals) {
if (interval[0] > originalIndex) {
break;
} else if (originalIndex <= interval[1]) {
compressedIndex+=(originalIndex-interval[0]);
break;
} else {
compressedIndex+=interval[2];
}
}
indexes[i] = compressedIndex;
int originalIndex = (chars[i] & 0xFFFF) - minBound;
indexes[i] = compressIndex(originalIndex);
}
return indexes;
}
public int getCharIndex(char c) {
int originalIndex = c & 0xFFFF;
return compressIndex(originalIndex);
}
private int compressIndex(int originalIndex) {
int compressedIndex = 0;
for (Integer[] interval : intervals) {
if (interval[0] > originalIndex) {
break;
} else if (originalIndex <= interval[1]) {
compressedIndex+=(originalIndex-interval[0]);
break;
} else {
compressedIndex+=interval[2];
}
}
return compressedIndex;
}
private int decompressIndex(int compressedIndex) {
int originalIndex = 0;
int i = 0;
for (Integer[] interval : intervals) {
i+=interval[2];
if (i == compressedIndex) {
return interval[1];
} else if (i > compressedIndex) {
return interval[1] - (i - compressedIndex);
}
}
return originalIndex;
}
@Override
public void initialize(GraphicEngine d) {}

View File

@ -169,7 +169,7 @@ public class GPUEngine implements GraphicEngine {
available = GLProfile.isAvailable(GLProfile.GL2ES2);
} catch (Exception ex) {
errored = true;
System.err.println(ex.getMessage());
System.err.println("OpenGL Error: "+ex.getMessage());
}
if (!available && !errored) {
System.err.println(GLProfile.glAvailabilityToString());

View File

@ -3,9 +3,14 @@ package org.warp.picalculator.gui.graphicengine.gpu;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import org.warp.picalculator.Utils;
import org.warp.picalculator.gui.graphicengine.BinaryFont;
import org.warp.picalculator.gui.graphicengine.GraphicEngine;
import org.warp.picalculator.gui.graphicengine.cpu.CPUFont;
@ -32,12 +37,13 @@ public class GPUFont implements BinaryFont {
public int minCharIndex;
public int maxCharIndex;
public LinkedList<Integer[]> intervals;
public int intervalsTotalSize = 0;
public int memoryWidth;
public int memoryHeight;
public int memoryWidthOfEachColumn;
private boolean initialized = false;
private ByteArrayOutputStream tmpFont;
private File tmpFont;
GPUFont(GraphicEngine g, String name) throws IOException {
this((GPUEngine) g, null, name);
@ -65,26 +71,59 @@ public class GPUFont implements BinaryFont {
minCharIndex = font.minBound;
maxCharIndex = font.maxBound;
intervals = font.intervals;
pregenTexture(font.rawchars);
intervalsTotalSize = font.intervalsTotalSize;
boolean[][] rawchars = font.rawchars;
font = null;
Utils.gc();
pregenTexture(rawchars);
rawchars = null;
Utils.gc();
}
public int[] getCharIndexes(String txt) {
final int l = txt.length();
final int[] indexes = new int[l];
final char[] chars = txt.toCharArray();
for (int i = 0; i < l; i++) {
indexes[i] = getCharIndex(chars[i]);
final int[] indexes = new int[txt.length()];
int i = 0;
for (char c : txt.toCharArray()) {
indexes[i] = compressIndex((c & 0xFFFF) - minCharIndex);
i++;
}
return indexes;
}
public int getCharIndex(char ch) {
return (ch & 0xFFFF) - minCharIndex;
public int getCharIndex(char c) {
int originalIndex = c & 0xFFFF;
return compressIndex(originalIndex);
}
private void pregenTexture(boolean[][] chars) {
final int totalChars = maxCharIndex - minCharIndex;
private int compressIndex(int originalIndex) {
int compressedIndex = 0;
for (Integer[] interval : intervals) {
if (interval[0] > originalIndex) {
break;
} else if (originalIndex <= interval[1]) {
compressedIndex+=(originalIndex-interval[0]);
break;
} else {
compressedIndex+=interval[2];
}
}
return compressedIndex;
}
private int decompressIndex(int compressedIndex) {
int originalIndex = 0;
int i = 0;
for (Integer[] interval : intervals) {
i+=interval[2];
if (i >= compressedIndex) {
return interval[1] - (i - compressedIndex);
}
}
return originalIndex;
}
private void pregenTexture(boolean[][] chars) throws IOException {
final int totalChars = this.intervalsTotalSize;
int w = powerOf2((int) (Math.ceil(Math.sqrt(totalChars) * charW)));
int h = powerOf2((int) (Math.ceil(Math.sqrt(totalChars) * charH)));
int maxIndexW = (int) Math.floor(((double) w) / ((double) charW)) - 1;
@ -102,21 +141,22 @@ public class GPUFont implements BinaryFont {
System.out.println(((int)Math.ceil(Math.sqrt(totalChars) * charW)) + " * " + ((int)Math.ceil(Math.sqrt(totalChars) * charH)) + " --> " + w + " * " + h);
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(w*h*8*4);
File f = Files.createTempFile("texture-font-", ".png").toFile();
final FileOutputStream outputStream = new FileOutputStream(f);
final ImageInfo imi = new ImageInfo(w, h, 8, true); // 8 bits per channel, alpha
// open image for writing to a output stream
final PngWriter png = new PngWriter(outputStream, imi);
for (int y = 0; y < png.imgInfo.rows; y++) {
ImageLineInt iline = new ImageLineInt(imi);
int[] xValues = new int[imi.cols];
for (int indexX = 0; indexX < maxIndexW; indexX++) {// this line will be written to all rows
for (int indexX = 0; indexX <= maxIndexW; indexX++) {// this line will be written to all rows
final int charY = (y % charH);
final int indexY = (y - charY)/charH;
final int i = indexY * maxIndexW + indexX;
final int i = indexY * (maxIndexW+1) + indexX - this.minCharIndex;
boolean[] currentChar;
if (i < totalChars && (currentChar=chars[i]) != null) {
for (int charX = 0; charX < charW; charX++) {
if (i > 0 & i < totalChars && currentChar != null && currentChar[charX + charY * charW]) {
if (i >= 0 & i < totalChars && currentChar != null && currentChar[charX + charY * charW]) {
xValues[indexX * charW + charX] = 0xFFFFFFFF;
}
// ImageLineHelper.setPixelRGBA8(iline, x, color, color, color, color);
@ -124,21 +164,25 @@ public class GPUFont implements BinaryFont {
}
}
ImageLineHelper.setPixelsRGBA8(iline, xValues);
if (y % 200 == 0) {
if (y % 10 == 0) {
System.out.println(y + "/" + png.imgInfo.rows);
}
png.writeRow(iline);
}
chars = null;
png.end();
Utils.gc();
try {
memoryWidth = w;
memoryHeight = h;
memoryWidthOfEachColumn = maxIndexW;
memoryWidthOfEachColumn = maxIndexW + 1;
textureW = w;
textureH = h;
outputStream.flush();
this.tmpFont = outputStream;
outputStream.close();
Utils.gc();
this.tmpFont = f;
} catch (GLException | IOException e) {
e.printStackTrace();
}
@ -146,7 +190,7 @@ public class GPUFont implements BinaryFont {
private void genTexture() {
try {
texture = GPURenderer.importTexture(tmpFont);
texture = GPURenderer.importTexture(tmpFont, true);
tmpFont = null;
} catch (GLException | IOException e) {
e.printStackTrace();

View File

@ -15,6 +15,7 @@ import java.nio.file.Paths;
import javax.imageio.ImageIO;
import org.warp.picalculator.StaticVars;
import org.warp.picalculator.Utils;
import org.warp.picalculator.gui.graphicengine.BinaryFont;
import org.warp.picalculator.gui.graphicengine.Renderer;
@ -231,6 +232,7 @@ public class GPURenderer implements Renderer {
return currentFont;
}
@Deprecated
static Texture importTexture(GL gl, String string) throws IOException {
final FileInputStream f = new FileInputStream("test.png");
final TextureData tx_dat = TextureIO.newTextureData(gl.getGLProfile(), f, false, TextureIO.PNG);
@ -242,37 +244,56 @@ public class GPURenderer implements Renderer {
return tex;
}
static OpenedTextureData openTexture(String file) throws GLException, IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
BufferedImage img = ImageIO.read(!Files.exists(Paths.get(file)) ? GPURenderer.class.getResource("/" + file) : new File(file).toURI().toURL());
ImageIO.write(img, "png", os);
return new OpenedTextureData(img.getWidth(), img.getHeight(), os);
static OpenedTextureData openTexture(String file, boolean isResource) throws GLException, IOException {
BufferedImage img = ImageIO.read(isResource ? GPURenderer.class.getResource("/" + file) : new File(file).toURI().toURL());
File f;
if (isResource) {
f = Files.createTempFile("texture-", ".png").toFile();
ImageIO.write(img, "png", f);
} else {
f = new File(file);
}
int imgW = img.getWidth();
int imgH = img.getHeight();
img = null;
Utils.gc();
return new OpenedTextureData(imgW, imgH, f, isResource);
}
public static class OpenedTextureData {
public final int w;
public final int h;
public final ByteArrayOutputStream os;
public final File f;
public final boolean deleteOnExit;
/**
* @param w
* @param h
* @param os
* @param f
* @param deleteOnExit
*/
public OpenedTextureData(int w, int h, ByteArrayOutputStream os) {
public OpenedTextureData(int w, int h, File f, boolean deleteOnExit) {
this.w = w;
this.h = h;
this.os = os;
this.f = f;
this.deleteOnExit = deleteOnExit;
}
}
static Texture importTexture(ByteArrayOutputStream os) throws GLException, IOException {
final InputStream fis = new ByteArrayInputStream(os.toByteArray());
final Texture tex = TextureIO.newTexture(fis, false, TextureIO.PNG);
static Texture importTexture(File f, boolean deleteOnExit) throws GLException, IOException {
final Texture tex = TextureIO.newTexture(f, false);
if (deleteOnExit && f.exists()) {
try {
if (StaticVars.debugOn) throw new IOException("Delete on exit!");
f.delete();
}catch (Exception ex) {
f.deleteOnExit();
}
}
tex.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
tex.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
os = null;
f = null;
return tex;
}

View File

@ -23,25 +23,28 @@ public class GPUSkin implements Skin {
private String texturePath;
private boolean initialized = false;
private boolean isResource;
GPUSkin(GraphicEngine d, String file) throws IOException {
load(file);
}
@Override
public void load(String file) throws IOException {
if (!Files.exists(Paths.get(file)) && (this.getClass().getClassLoader().getResource(file)) == null) {
boolean isResource = !Files.exists(Paths.get(file));
if (isResource && (this.getClass().getClassLoader().getResource(file)) == null) {
throw new IOException("File '" + file + "' not found!");
}
texturePath = file;
this.isResource = isResource;
}
@Override
public void initialize(GraphicEngine d) {
try {
final OpenedTextureData i = GPURenderer.openTexture(texturePath);
final OpenedTextureData i = GPURenderer.openTexture(texturePath, isResource);
final GL2ES1 gl = GPURenderer.gl;
t = GPURenderer.importTexture(i.os);
t = GPURenderer.importTexture(i.f, i.deleteOnExit);
w = i.w;
h = i.h;
t.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);

View File

@ -19,7 +19,7 @@ public class MarioScreen extends Screen {
private int easterNum = 0;
private float easterElapsed = 0;
private int easterMax = 21;
private String[] easterFu32 = new String[] {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g"};
private String[] easterFu32 = new String[] {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "ò"};
private int easterFu32Num = 0;
private float easterFu32Elapsed = 0;
private boolean errored;
@ -48,17 +48,17 @@ public class MarioScreen extends Screen {
groundskin = DisplayManager.INSTANCE.engine.loadSkin("marioground.png");
if (easterfont == null)
try {
easterfont = DisplayManager.INSTANCE.engine.loadFont("X:\\CESTINO\\ALTRO", "easter");
easterfont = DisplayManager.INSTANCE.engine.loadFont("easter");
} catch (Exception ex) {}
if (fu32font == null)
try {
fu32font = DisplayManager.INSTANCE.engine.loadFont("X:\\CESTINO\\ALTRO", "fu32");
fu32font = DisplayManager.INSTANCE.engine.loadFont("fu32");
} catch (Exception ex) {
ex.printStackTrace();
}
if (easterskin == null)
try {
easterskin = DisplayManager.INSTANCE.engine.loadSkin("X:\\CESTINO\\ALTRO\\font_easter.png");
easterskin = DisplayManager.INSTANCE.engine.loadSkin("font_easter.png");
} catch (Exception ex) {}
} catch (final IOException e) {
e.printStackTrace();