From e45acd967be9fb5461cc58f5ade6cd4df63d681e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Thu, 14 Apr 2016 22:40:37 +0200 Subject: [PATCH] Updated char width map for new font and made the width file resource-packable. Closes #1760. --- src/main/resources/application.conf | 10 ++++----- .../assets/opencomputers/wcwidth.bin | Bin 65537 -> 65536 bytes src/main/scala/li/cil/oc/Settings.scala | 2 +- .../renderer/font/DynamicFontRenderer.scala | 2 +- ...tParserUnifont.java => FontParserHex.java} | 19 +++++++++++------- .../client/renderer/font/IGlyphProvider.java | 10 ++++----- src/main/scala/li/cil/oc/util/FontUtils.scala | 10 ++++++--- 7 files changed, 31 insertions(+), 22 deletions(-) rename src/main/scala/li/cil/oc/client/renderer/font/{FontParserUnifont.java => FontParserHex.java} (78%) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index c0f9ceeb8..a662f9621 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -72,10 +72,10 @@ opencomputers { # Defaults to white, feel free to make it some other color, tho! monochromeColor: 0xFFFFFF - # Which font renderer to use. Defaults to `unifont` if invalid. + # Which font renderer to use. Defaults to `hexfont` if invalid. # Possible values: - # - unifont: the (since 1.3.2) default font renderer. Based on the - # Unifont and capable of rendering many unicode glyphs. + # - hexfont: the (since 1.3.2) default font renderer. Font in .hex format + # capable of rendering many unicode glyphs. # The used font data can be swapped out using resource packs, # but is harder to work with, since it involves binary data. # - 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 # importantly, can only render code page 437 (as opposed to... # a *lot* of unicode). - fontRenderer: "unifont" + fontRenderer=hexfont # The sample rate used for generating beeps of computers' internal # 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 # width from what wcwidth says). - logUnifontErrors: false + logHexFontErrors: false # Extract the native library with Lua into the system's temporary # directory instead of the game directory (e.g. /tmp on Linux). The diff --git a/src/main/resources/assets/opencomputers/wcwidth.bin b/src/main/resources/assets/opencomputers/wcwidth.bin index 9339c9b4eb34888255c28db2ae6c518cf5a9ffa8..7fa116d286f1bf6a7fe5fcf95d405d28ef4e2b3b 100644 GIT binary patch literal 65536 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 diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index f9ca26017..8eb7f165d 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -405,7 +405,7 @@ class Settings(val config: Config) { val logFullLibLoadErrors = config.getBoolean("debug.logFullNativeLibLoadErrors") val forceNativeLib = config.getString("debug.forceNativeLibWithName") 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 debugPersistence = config.getBoolean("debug.verbosePersistenceErrors") val nativeInTmpDir = config.getBoolean("debug.nativeInTmpDir") diff --git a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala index f04cf39f3..0d3df8a69 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala @@ -19,7 +19,7 @@ import scala.collection.mutable */ class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloadListener { private val glyphProvider: IGlyphProvider = Settings.get.fontRenderer match { - case _ => new FontParserUnifont() + case _ => new FontParserHex() } private val textures = mutable.ArrayBuffer.empty[CharTexture] diff --git a/src/main/scala/li/cil/oc/client/renderer/font/FontParserUnifont.java b/src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java similarity index 78% rename from src/main/scala/li/cil/oc/client/renderer/font/FontParserUnifont.java rename to src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java index caf7c9715..21909c33b 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/FontParserUnifont.java +++ b/src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java @@ -13,7 +13,7 @@ import java.io.InputStream; import java.io.InputStreamReader; 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[] TRANSPARENT = {0, 0, 0, 0}; @@ -26,17 +26,19 @@ public class FontParserUnifont implements IGlyphProvider { } try { 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 { - OpenComputers.log().info("Initializing Unifont glyph provider."); + OpenComputers.log().info("Initializing unicode glyph provider."); final BufferedReader input = new BufferedReader(new InputStreamReader(font)); String line; int glyphCount = 0; while ((line = input.readLine()) != null) { final String[] info = line.split(":"); final int charCode = Integer.parseInt(info[0], 16); + if (charCode < 0 || charCode >= glyphs.length) continue; // Out of bounds. final int expectedWidth = FontUtils.wcwidth(charCode); 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 int glyphWidth = glyph.length / getGlyphHeight(); if (expectedWidth == glyphWidth) { @@ -47,15 +49,16 @@ public class FontParserUnifont implements IGlyphProvider { glyphCount++; } glyphs[charCode] = glyph; - } else if (Settings.get().logUnifontErrors()) { - 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)); + } else if (Settings.get().logHexFontErrors()) { + 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."); } finally { try { font.close(); - } catch (IOException ignored) { + } catch (IOException ex) { + OpenComputers.log().warn("Error parsing font.", ex); } } } catch (IOException ex) { @@ -71,8 +74,10 @@ public class FontParserUnifont implements IGlyphProvider { final ByteBuffer buffer = BufferUtils.createByteBuffer(glyph.length * getGlyphWidth() * 4); for (byte aGlyph : glyph) { int c = ((int) aGlyph) & 0xFF; + // Grab all bits by grabbing the leftmost one then shifting. 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); c <<= 1; } diff --git a/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java b/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java index cba6960c4..230b2bab4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java +++ b/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java @@ -12,7 +12,7 @@ public interface IGlyphProvider { *

* 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. @@ -30,9 +30,9 @@ public interface IGlyphProvider { * * @param charCode the char to get the render glyph data for. * @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. @@ -41,12 +41,12 @@ public interface IGlyphProvider { * 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). */ - public int getGlyphWidth(); + int getGlyphWidth(); /** * Get the glyph height for this provider, in pixels. *

* Each glyph provided is expected to have the same height. */ - public int getGlyphHeight(); + int getGlyphHeight(); } diff --git a/src/main/scala/li/cil/oc/util/FontUtils.scala b/src/main/scala/li/cil/oc/util/FontUtils.scala index dcc614c84..a3a5a637b 100644 --- a/src/main/scala/li/cil/oc/util/FontUtils.scala +++ b/src/main/scala/li/cil/oc/util/FontUtils.scala @@ -1,8 +1,12 @@ package li.cil.oc.util import java.io.IOException +import java.io.InputStream import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import net.minecraft.client.Minecraft +import net.minecraft.util.ResourceLocation object FontUtils { // 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! private val widths = { val ba = Array.fill[Byte](0x10000)(-1) - val is = FontUtils.getClass.getResourceAsStream("/assets/opencomputers/wcwidth.bin") - if (is != null) { - try { + Minecraft.getMinecraft.getResourceManager.getResource(new ResourceLocation(Settings.resourceDomain, "wcwidth.bin")).getInputStream match { + case is: InputStream => try { is.read(ba) is.close() } catch { case e: IOException => OpenComputers.log.warn("Failed loading character widths. Font rendering will probably be derpy as all hell.", e) } + case _ => // Null. } ba }