From 2bfd15ad2ec918e03c9b181b66cfec4a621e0054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 12 Jul 2014 14:15:55 +0200 Subject: [PATCH] Cleaning up a bit and getting rid of an unnecessary copy step. --- .../renderer/TextBufferRenderCache.scala | 2 +- .../renderer/font/DataCharRenderer.scala | 45 ------ .../renderer/font/DynamicCharRenderer.scala | 11 -- .../renderer/font/DynamicFontRenderer.scala | 49 +++---- .../renderer/font/FontCharRenderer.scala | 132 ------------------ 5 files changed, 26 insertions(+), 213 deletions(-) delete mode 100644 src/main/scala/li/cil/oc/client/renderer/font/DataCharRenderer.scala delete mode 100644 src/main/scala/li/cil/oc/client/renderer/font/DynamicCharRenderer.scala delete mode 100644 src/main/scala/li/cil/oc/client/renderer/font/FontCharRenderer.scala diff --git a/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala index 65938cae8..0ad9e5596 100644 --- a/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala +++ b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala @@ -16,7 +16,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti val renderer = // new font.StaticFontRenderer() // new font.DynamicFontRenderer(new FontCharRenderer("Terminal", 11)) - new DynamicFontRenderer(new font.DataCharRenderer()) + new DynamicFontRenderer() private val cache = com.google.common.cache.CacheBuilder.newBuilder(). expireAfterAccess(2, TimeUnit.SECONDS). diff --git a/src/main/scala/li/cil/oc/client/renderer/font/DataCharRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/DataCharRenderer.scala deleted file mode 100644 index b9e4a773b..000000000 --- a/src/main/scala/li/cil/oc/client/renderer/font/DataCharRenderer.scala +++ /dev/null @@ -1,45 +0,0 @@ -package li.cil.oc.client.renderer.font - -import li.cil.oc.util.FontUtil -import org.lwjgl.opengl.GL11 - -class DataCharRenderer extends DynamicCharRenderer { - val parser = new FontParserUnifont() - - override def canDisplay(c: Char) = FontUtil.wcwidth(c) > 0 && parser.getGlyph(c) != null - - override def charWidth: Double = parser.getGlyphWidth - - override def charHeight: Double = parser.getGlyphHeight - - override def drawChar(charCode: Int) { - val w = parser.getGlyphWidth * FontUtil.wcwidth(charCode) - val h = parser.getGlyphHeight - - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) - GL11.glColor4f(1, 1, 1, 1) - GL11.glEnable(GL11.GL_BLEND) - - val texture = GL11.glGenTextures() - GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture) - GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, w, h, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, parser.getGlyph(charCode)) - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST) - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR) - - GL11.glBegin(GL11.GL_QUADS) - GL11.glTexCoord2f(0, 1) - GL11.glVertex2f(0, h) - GL11.glTexCoord2f(1, 1) - GL11.glVertex2f(w, h) - GL11.glTexCoord2f(1, 0) - GL11.glVertex2f(w, 0) - GL11.glTexCoord2f(0, 0) - GL11.glVertex2f(0, 0) - GL11.glEnd() - - GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0) - GL11.glDeleteTextures(texture) - - GL11.glPopAttrib() - } -} diff --git a/src/main/scala/li/cil/oc/client/renderer/font/DynamicCharRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/DynamicCharRenderer.scala deleted file mode 100644 index c89a1f604..000000000 --- a/src/main/scala/li/cil/oc/client/renderer/font/DynamicCharRenderer.scala +++ /dev/null @@ -1,11 +0,0 @@ -package li.cil.oc.client.renderer.font - -trait DynamicCharRenderer { - def canDisplay(c: Char): Boolean - - def charWidth: Double - - def charHeight: Double - - def drawChar(charCode: Int) -} 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 08a516636..94aed8b57 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 @@ -1,8 +1,5 @@ package li.cil.oc.client.renderer.font -import java.awt.Font -import java.io.InputStream - import li.cil.oc.client.renderer.font.DynamicFontRenderer.CharTexture import li.cil.oc.util.{FontUtil, RenderState} import org.lwjgl.BufferUtils @@ -14,7 +11,9 @@ import scala.collection.mutable * Font renderer that dynamically generates lookup textures by rendering a font * to it. It's pretty broken right now, and font rendering looks crappy as hell. */ -class DynamicFontRenderer(val charRenderer: DynamicCharRenderer) extends TextureFontRenderer { +class DynamicFontRenderer extends TextureFontRenderer { + private val glyphProvider = new FontParserUnifont() + private val textures = mutable.ArrayBuffer(new DynamicFontRenderer.CharTexture(this)) private val charMap = mutable.Map.empty[Char, DynamicFontRenderer.CharIcon] @@ -38,9 +37,9 @@ class DynamicFontRenderer(val charRenderer: DynamicCharRenderer) extends Texture RenderState.checkError(getClass.getName + ".: glGenFramebuffers") - override protected def charWidth = charRenderer.charWidth.toInt + override protected def charWidth = glyphProvider.getGlyphWidth.toInt - override protected def charHeight = charRenderer.charHeight.toInt + override protected def charHeight = glyphProvider.getGlyphHeight.toInt override protected def textureCount = textures.length @@ -62,7 +61,7 @@ class DynamicFontRenderer(val charRenderer: DynamicCharRenderer) extends Texture } private def createCharIcon(char: Char): DynamicFontRenderer.CharIcon = { - if (!charRenderer.canDisplay(char)) { + if (FontUtil.wcwidth(char) < 1 || glyphProvider.getGlyph(char) == null) { if (char == '?') null else charMap.getOrElseUpdate('?', createCharIcon('?')) } @@ -93,8 +92,8 @@ object DynamicFontRenderer { private val cellHeight = owner.charHeight + 2 private val cols = size / cellWidth private val rows = size / cellHeight - private val uStep = cellWidth / size.toFloat - private val vStep = cellHeight / size.toFloat + private val uStep = cellWidth / size.toDouble + private val vStep = cellHeight / size.toDouble private val pad = 1.0 / size private val capacity = cols * rows @@ -107,11 +106,14 @@ object DynamicFontRenderer { def isFull = chars >= capacity def add(char: Char) = { - // TODO force to next row if wide char and won't fit into row. + val glyphWidth = FontUtil.wcwidth(char) + val w = owner.charWidth * glyphWidth + val h = owner.charHeight + if (chars + glyphWidth > cols) { + chars += 1 + } val x = chars % cols val y = chars / cols - val w = owner.charWidth * FontUtil.wcwidth(char) - val h = owner.charHeight GL11.glDisable(GL11.GL_DEPTH_TEST) GL11.glDepthMask(false) @@ -121,7 +123,7 @@ object DynamicFontRenderer { GL20.glDrawBuffers(GL30.GL_COLOR_ATTACHMENT0) GL11.glClear(GL11.GL_COLOR_BUFFER_BIT) - GL11.glViewport(0, 0, owner.charWidth, h) + GL11.glViewport(0, 0, w, h) GL11.glMatrixMode(GL11.GL_PROJECTION) GL11.glPushMatrix() @@ -134,9 +136,8 @@ object DynamicFontRenderer { GL11.glLoadIdentity() GL11.glTranslatef(0, 0, -0.5f) - owner.charRenderer.drawChar(char) - - GL43.glCopyImageSubData(owner.rbo, GL30.GL_RENDERBUFFER, 0, 0, 0, 0, id, GL11.GL_TEXTURE_2D, 0, 1 + x * cellWidth, 1 + y * cellHeight, 0, w, h, 1) + GL11.glBindTexture(GL11.GL_TEXTURE_2D, id) + GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 1 + x * cellWidth, 1 + y * cellHeight, w, h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, owner.glyphProvider.getGlyph(char)) GL11.glMatrixMode(GL11.GL_PROJECTION) GL11.glPopMatrix() @@ -145,21 +146,21 @@ object DynamicFontRenderer { GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0) - chars += FontUtil.wcwidth(char) + chars += glyphWidth - new CharIcon(this, w, h, pad + x * uStep, pad + y * vStep, (x + 1) * uStep - 2 * pad, (y + 1) * vStep - 2 * pad) + new CharIcon(this, w, h, pad + x * uStep, pad + y * vStep, (x + glyphWidth) * uStep - pad, (y + 1) * vStep - pad) } } class CharIcon(val texture: CharTexture, val w: Int, val h: Int, val u1: Double, val v1: Double, val u2: Double, val v2: Double) { def draw(tx: Float, ty: Float) { - GL11.glTexCoord2d(u1, v1) - GL11.glVertex2f(tx, ty + h) - GL11.glTexCoord2d(u2, v1) - GL11.glVertex2f(tx + w, ty + h) - GL11.glTexCoord2d(u2, v2) - GL11.glVertex2f(tx + w, ty) GL11.glTexCoord2d(u1, v2) + GL11.glVertex2f(tx, ty + h) + GL11.glTexCoord2d(u2, v2) + GL11.glVertex2f(tx + w, ty + h) + GL11.glTexCoord2d(u2, v1) + GL11.glVertex2f(tx + w, ty) + GL11.glTexCoord2d(u1, v1) GL11.glVertex2f(tx, ty) } } diff --git a/src/main/scala/li/cil/oc/client/renderer/font/FontCharRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/FontCharRenderer.scala deleted file mode 100644 index fa1d1c19c..000000000 --- a/src/main/scala/li/cil/oc/client/renderer/font/FontCharRenderer.scala +++ /dev/null @@ -1,132 +0,0 @@ -package li.cil.oc.client.renderer.font - -import java.awt.Font -import java.awt.font.FontRenderContext -import java.awt.geom.{PathIterator, Point2D} -import java.io.InputStream - -import org.lwjgl.opengl.GL11 -import org.lwjgl.util.glu.{GLU, GLUtessellatorCallbackAdapter} - -class FontCharRenderer(val font: Font) extends DynamicCharRenderer { - def this(name: String, size: Int) = this(new Font(name, Font.PLAIN, size)) - - def this(stream: InputStream, size: Int) = this(Font.createFont(Font.TRUETYPE_FONT, stream).deriveFont(size)) - - private val context = new FontRenderContext(font.getTransform, true, true) - private val callback = new FontCharRenderer.Callback() - private val maxCharBounds = font.getMaxCharBounds(context) - - def canDisplay(c: Char) = font.canDisplay(c) - - def charWidth = maxCharBounds.getWidth - - def charHeight = maxCharBounds.getHeight - - def drawChar(charCode: Int) { - GL11.glPushMatrix() - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) - GL11.glTranslated(-maxCharBounds.getX, -maxCharBounds.getY, 0) - GL11.glDisable(GL11.GL_TEXTURE_2D) - GL11.glColor4f(1, 1, 1, 1) - - val vector = font.createGlyphVector(context, Array(charCode.toChar)) - - val tess = GLU.gluNewTess() - tess.gluTessCallback(GLU.GLU_TESS_BEGIN, callback) - tess.gluTessCallback(GLU.GLU_TESS_END, callback) - tess.gluTessCallback(GLU.GLU_TESS_VERTEX, callback) - tess.gluTessCallback(GLU.GLU_TESS_EDGE_FLAG, callback) - tess.gluTessNormal(0, 0, -1) - - for (i <- 0 until vector.getNumGlyphs) { - val outline = vector.getGlyphOutline(i).getPathIterator(null) - tess.gluTessBeginPolygon(null) - val current = new Point2D.Double(0, 0) - val coords = new Array[Double](6) - while (!outline.isDone) { - if (outline.getWindingRule == PathIterator.WIND_EVEN_ODD) - tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_ODD) - else - tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_NONZERO) - outline.currentSegment(coords) match { - case PathIterator.SEG_MOVETO => - tess.gluTessBeginContour() - current.setLocation(coords(0), coords(1)) - case PathIterator.SEG_LINETO => - val buffer = Array[Float](coords(0).toFloat, coords(1).toFloat) - tess.gluTessVertex(coords, 0, buffer) - current.setLocation(coords(0), coords(1)) - case PathIterator.SEG_QUADTO => - // From SEG_QUADTO: - // P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2 - // with 0 <= t <= 1 - // B(n,m) = mth coefficient of nth degree Bernstein polynomial - // = C(n,m) * t^(m) * (1 - t)^(n-m) - // C(n,m) = Combinations of n things, taken m at a time - // = n! / (m! * (n-m)!) - // So: - // P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2 - // = C(2,0) * t^(0) * (1 - t)^(2-0) * CP + - // C(2,1) * t^(1) * (1 - t)^(2-1) * P1 + - // C(2,2) * t^(2) * (1 - t)^(2-2) * P2 - // = 2! / (0! * (2-0)!) * (1 - t)^2 * CP + - // 2! / (1! * (2-1)!) * t * (1 - t) * P1 + - // 2! / (2! * (2-2)!) * t^2 * P2 - // = (1 - t)^2 * CP + - // 2 * t * (1 - t) * P1 + - // t^2 * P2 - // = (1 - 2*t + t^2) * CP + - // 2 * (t - t^2) * P1 + - // t^2 * P2 - val interpolated = new Array[Double](3) - def p(t: Double) = { - val tSquared = t * t - val fc = 1 - 2 * t + tSquared - val f1 = 2 * (t - tSquared) - val f2 = tSquared - interpolated(0) = fc * current.x + f1 * coords(0) + f2 * coords(2) - interpolated(1) = fc * current.y + f1 * coords(1) + f2 * coords(3) - val buffer = Array[Float](interpolated(0).toFloat, interpolated(1).toFloat) - tess.gluTessVertex(interpolated, 0, buffer) - } - for (t <- 0.0 until 1.0 by 0.25) { - p(t) - } - current.setLocation(coords(2), coords(3)) - case PathIterator.SEG_CUBICTO => - // Not supported. - case PathIterator.SEG_CLOSE => - tess.gluTessEndContour() - case _ => // Wuh? - } - outline.next() - } - tess.gluTessEndPolygon() - } - - tess.gluDeleteTess() - - GL11.glPopAttrib() - GL11.glPopMatrix() - } -} - -object FontCharRenderer { - - private class Callback extends GLUtessellatorCallbackAdapter { - override def begin(mode: Int) = GL11.glBegin(mode) - - override def end() = GL11.glEnd() - - override def vertex(coords: scala.Any) { - val point = coords.asInstanceOf[Array[Float]] - GL11.glVertex2f(point(0), point(1)) - } - - override def edgeFlag(boundaryEdge: Boolean) { - GL11.glEdgeFlag(boundaryEdge) - } - } - -} \ No newline at end of file