Updated char width map for new font and made the width file resource-packable. Closes #1760.

This commit is contained in:
Florian Nücke 2016-04-14 22:40:37 +02:00
parent d24d70554b
commit e45acd967b
7 changed files with 31 additions and 22 deletions

View File

@ -72,10 +72,10 @@ opencomputers {
# Defaults to white, feel free to make it some other color, tho! # Defaults to white, feel free to make it some other color, tho!
monochromeColor: 0xFFFFFF monochromeColor: 0xFFFFFF
# Which font renderer to use. Defaults to `unifont` if invalid. # Which font renderer to use. Defaults to `hexfont` if invalid.
# Possible values: # Possible values:
# - unifont: the (since 1.3.2) default font renderer. Based on the # - hexfont: the (since 1.3.2) default font renderer. Font in .hex format
# Unifont and capable of rendering many unicode glyphs. # capable of rendering many unicode glyphs.
# The used font data can be swapped out using resource packs, # The used font data can be swapped out using resource packs,
# but is harder to work with, since it involves binary data. # but is harder to work with, since it involves binary data.
# - texture: the old, font-texture based font renderer that was used # - texture: the old, font-texture based font renderer that was used
@ -84,7 +84,7 @@ opencomputers {
# is slightly less efficient than the new one, and more # is slightly less efficient than the new one, and more
# importantly, can only render code page 437 (as opposed to... # importantly, can only render code page 437 (as opposed to...
# a *lot* of unicode). # a *lot* of unicode).
fontRenderer: "unifont" fontRenderer=hexfont
# The sample rate used for generating beeps of computers' internal # The sample rate used for generating beeps of computers' internal
# speakers. Use custom values at your own responsibility here; if it # speakers. Use custom values at your own responsibility here; if it
@ -1480,7 +1480,7 @@ opencomputers {
# Logs information about malformed glyphs (i.e. glyphs that deviate in # Logs information about malformed glyphs (i.e. glyphs that deviate in
# width from what wcwidth says). # width from what wcwidth says).
logUnifontErrors: false logHexFontErrors: false
# Extract the native library with Lua into the system's temporary # Extract the native library with Lua into the system's temporary
# directory instead of the game directory (e.g. /tmp on Linux). The # directory instead of the game directory (e.g. /tmp on Linux). The

View File

@ -405,7 +405,7 @@ class Settings(val config: Config) {
val logFullLibLoadErrors = config.getBoolean("debug.logFullNativeLibLoadErrors") val logFullLibLoadErrors = config.getBoolean("debug.logFullNativeLibLoadErrors")
val forceNativeLib = config.getString("debug.forceNativeLibWithName") val forceNativeLib = config.getString("debug.forceNativeLibWithName")
val logOpenGLErrors = config.getBoolean("debug.logOpenGLErrors") val logOpenGLErrors = config.getBoolean("debug.logOpenGLErrors")
val logUnifontErrors = config.getBoolean("debug.logUnifontErrors") val logHexFontErrors = config.getBoolean("debug.logHexFontErrors")
val alwaysTryNative = config.getBoolean("debug.alwaysTryNative") val alwaysTryNative = config.getBoolean("debug.alwaysTryNative")
val debugPersistence = config.getBoolean("debug.verbosePersistenceErrors") val debugPersistence = config.getBoolean("debug.verbosePersistenceErrors")
val nativeInTmpDir = config.getBoolean("debug.nativeInTmpDir") val nativeInTmpDir = config.getBoolean("debug.nativeInTmpDir")

View File

@ -19,7 +19,7 @@ import scala.collection.mutable
*/ */
class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloadListener { class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloadListener {
private val glyphProvider: IGlyphProvider = Settings.get.fontRenderer match { private val glyphProvider: IGlyphProvider = Settings.get.fontRenderer match {
case _ => new FontParserUnifont() case _ => new FontParserHex()
} }
private val textures = mutable.ArrayBuffer.empty[CharTexture] private val textures = mutable.ArrayBuffer.empty[CharTexture]

View File

@ -13,7 +13,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public class FontParserUnifont implements IGlyphProvider { public class FontParserHex implements IGlyphProvider {
private static final byte[] OPAQUE = {(byte) 255, (byte) 255, (byte) 255, (byte) 255}; private static final byte[] OPAQUE = {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
private static final byte[] TRANSPARENT = {0, 0, 0, 0}; private static final byte[] TRANSPARENT = {0, 0, 0, 0};
@ -26,17 +26,19 @@ public class FontParserUnifont implements IGlyphProvider {
} }
try { try {
final InputStream font = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation(Settings.resourceDomain(), "font.hex")).getInputStream(); final InputStream font = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation(Settings.resourceDomain(), "font.hex")).getInputStream();
OpenComputers.log().info("Initialized Unifont glyph provider."); OpenComputers.log().info("Initialized unicode glyph provider.");
try { try {
OpenComputers.log().info("Initializing Unifont glyph provider."); OpenComputers.log().info("Initializing unicode glyph provider.");
final BufferedReader input = new BufferedReader(new InputStreamReader(font)); final BufferedReader input = new BufferedReader(new InputStreamReader(font));
String line; String line;
int glyphCount = 0; int glyphCount = 0;
while ((line = input.readLine()) != null) { while ((line = input.readLine()) != null) {
final String[] info = line.split(":"); final String[] info = line.split(":");
final int charCode = Integer.parseInt(info[0], 16); final int charCode = Integer.parseInt(info[0], 16);
if (charCode < 0 || charCode >= glyphs.length) continue; // Out of bounds.
final int expectedWidth = FontUtils.wcwidth(charCode); final int expectedWidth = FontUtils.wcwidth(charCode);
if (expectedWidth < 1) continue; // Skip control characters. if (expectedWidth < 1) continue; // Skip control characters.
// Two chars representing one byte represent one row of eight pixels.
final byte[] glyph = new byte[info[1].length() >> 1]; final byte[] glyph = new byte[info[1].length() >> 1];
final int glyphWidth = glyph.length / getGlyphHeight(); final int glyphWidth = glyph.length / getGlyphHeight();
if (expectedWidth == glyphWidth) { if (expectedWidth == glyphWidth) {
@ -47,15 +49,16 @@ public class FontParserUnifont implements IGlyphProvider {
glyphCount++; glyphCount++;
} }
glyphs[charCode] = glyph; glyphs[charCode] = glyph;
} else if (Settings.get().logUnifontErrors()) { } else if (Settings.get().logHexFontErrors()) {
OpenComputers.log().warn(String.format("Size of glyph for code point U+%04X (%s) in Unifont (%d) does not match expected width (%d), ignoring.", charCode, String.valueOf((char) charCode), glyphWidth, expectedWidth)); OpenComputers.log().warn(String.format("Size of glyph for code point U+%04X (%s) in font (%d) does not match expected width (%d), ignoring.", charCode, String.valueOf((char) charCode), glyphWidth, expectedWidth));
} }
} }
OpenComputers.log().info("Loaded " + glyphCount + " glyphs."); OpenComputers.log().info("Loaded " + glyphCount + " glyphs.");
} finally { } finally {
try { try {
font.close(); font.close();
} catch (IOException ignored) { } catch (IOException ex) {
OpenComputers.log().warn("Error parsing font.", ex);
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
@ -71,8 +74,10 @@ public class FontParserUnifont implements IGlyphProvider {
final ByteBuffer buffer = BufferUtils.createByteBuffer(glyph.length * getGlyphWidth() * 4); final ByteBuffer buffer = BufferUtils.createByteBuffer(glyph.length * getGlyphWidth() * 4);
for (byte aGlyph : glyph) { for (byte aGlyph : glyph) {
int c = ((int) aGlyph) & 0xFF; int c = ((int) aGlyph) & 0xFF;
// Grab all bits by grabbing the leftmost one then shifting.
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
if ((c & 128) > 0) buffer.put(OPAQUE); final boolean isBitSet = (c & 0x80) > 0;
if (isBitSet) buffer.put(OPAQUE);
else buffer.put(TRANSPARENT); else buffer.put(TRANSPARENT);
c <<= 1; c <<= 1;
} }

View File

@ -12,7 +12,7 @@ public interface IGlyphProvider {
* <p/> * <p/>
* This should usually also be called from the implementation's constructor. * This should usually also be called from the implementation's constructor.
*/ */
public void initialize(); void initialize();
/** /**
* Get a byte array of RGBA data describing the specified char. * Get a byte array of RGBA data describing the specified char.
@ -30,9 +30,9 @@ public interface IGlyphProvider {
* *
* @param charCode the char to get the render glyph data for. * @param charCode the char to get the render glyph data for.
* @return the RGBA byte array representing the char. * @return the RGBA byte array representing the char.
* @see li.cil.oc.client.renderer.font.FontParserUnifont#getGlyph(int) See the Unifont parser for a reference implementation. * @see FontParserHex#getGlyph(int) See the hexfont parser for a reference implementation.
*/ */
public ByteBuffer getGlyph(int charCode); ByteBuffer getGlyph(int charCode);
/** /**
* Get the single-width glyph width for this provider, in pixels. * Get the single-width glyph width for this provider, in pixels.
@ -41,12 +41,12 @@ public interface IGlyphProvider {
* a glyphs actual width (in pixels) is expected to be this value times * a glyphs actual width (in pixels) is expected to be this value times
* {@link li.cil.oc.util.FontUtils#wcwidth(int)} (for a specific char). * {@link li.cil.oc.util.FontUtils#wcwidth(int)} (for a specific char).
*/ */
public int getGlyphWidth(); int getGlyphWidth();
/** /**
* Get the glyph height for this provider, in pixels. * Get the glyph height for this provider, in pixels.
* <p/> * <p/>
* Each glyph provided is expected to have the same height. * Each glyph provided is expected to have the same height.
*/ */
public int getGlyphHeight(); int getGlyphHeight();
} }

View File

@ -1,8 +1,12 @@
package li.cil.oc.util package li.cil.oc.util
import java.io.IOException import java.io.IOException
import java.io.InputStream
import li.cil.oc.OpenComputers import li.cil.oc.OpenComputers
import li.cil.oc.Settings
import net.minecraft.client.Minecraft
import net.minecraft.util.ResourceLocation
object FontUtils { object FontUtils {
// Note: we load the widths from a file (one byte per width) because the Scala // Note: we load the widths from a file (one byte per width) because the Scala
@ -11,14 +15,14 @@ object FontUtils {
// who would have known! // who would have known!
private val widths = { private val widths = {
val ba = Array.fill[Byte](0x10000)(-1) val ba = Array.fill[Byte](0x10000)(-1)
val is = FontUtils.getClass.getResourceAsStream("/assets/opencomputers/wcwidth.bin") Minecraft.getMinecraft.getResourceManager.getResource(new ResourceLocation(Settings.resourceDomain, "wcwidth.bin")).getInputStream match {
if (is != null) { case is: InputStream => try {
try {
is.read(ba) is.read(ba)
is.close() is.close()
} catch { } catch {
case e: IOException => OpenComputers.log.warn("Failed loading character widths. Font rendering will probably be derpy as all hell.", e) case e: IOException => OpenComputers.log.warn("Failed loading character widths. Font rendering will probably be derpy as all hell.", e)
} }
case _ => // Null.
} }
ba ba
} }