Cleaning up a bit and getting rid of an unnecessary copy step.

This commit is contained in:
Florian Nücke 2014-07-12 14:15:55 +02:00
parent 1ef1c9ce00
commit 2bfd15ad2e
5 changed files with 26 additions and 213 deletions

View File

@ -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).

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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 + ".<init>: 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)
}
}

View File

@ -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)
}
}
}