PurpleKingdomGames / indigo

An FP game engine for Scala.
https://indigoengine.io/
MIT License
630 stars 58 forks source link

Generate Fonts in the plugin #726

Closed davesmith00000 closed 5 months ago

davesmith00000 commented 5 months ago

I haven't tried this (by @mprevel), but it's an interesting idea and work looking into.

import java.awt._
import java.awt.image._
import java.io._
import javax.imageio.ImageIO

object Font2Bitmap {

  val fontFile          = "some.ttf"
  val fontSize          = 24
  val charaterCodes     = 32 to 255
  val charactersPerLine = 16
  val color             = Color.BLACK
  val antiAlias         = true

  def main(args: Array[String]): Unit =
    try {

      val font =
        Font
          .createFont(Font.TRUETYPE_FONT, new File(fontFile))
          .deriveFont(fontSize.toFloat)

      val tmpBuffer = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
      val tmpG2d    = tmpBuffer.createGraphics()
      tmpG2d.setFont(font)
      val fontMetrics = tmpG2d.getFontMetrics()
      val charWidth   = fontMetrics.charWidth('A')
      val charHeight  = fontMetrics.getHeight()
      tmpG2d.dispose()

      val width  = charWidth * charactersPerLine
      val height = charHeight * ((charaterCodes.size + charactersPerLine - 1) / charactersPerLine)

      val bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
      val g2d           = bufferedImage.createGraphics()

      if (antiAlias)
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
      g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)

      g2d.setFont(font)
      g2d.setColor(color)

      val baselineOffset = fontMetrics.getLeading() + fontMetrics.getAscent()

      charaterCodes.foreach { code =>
        val n = code - charaterCodes.start
        val c = code.toChar.toString
        val x = (n % charactersPerLine) * charWidth
        val y = (n / charactersPerLine) * charHeight + baselineOffset
        g2d.drawString(c, x, y)
      }
      g2d.dispose()

      val file = new File(s"output_${fontSize}.png")
      ImageIO.write(bufferedImage, "PNG", file)
    } catch {
      case e: Exception => e.printStackTrace()
    }

}

      println(
        s"""
           |    val width = ${width}
           |    val height = ${height}
           |    val charWidth = ${charWidth}
           |    val charHeight = ${charHeight}
           |    val perLine = ${charactersPerLine}
           |    val startAt = ${charaterCodes.start}
           |    val endAt = ${charaterCodes.end}
           |
           |    def fontChar(i: Int) = {
           |      val n = i - startAt
           |      FontChar(
           |        character = i.toChar.toString,
           |        x = n % perLine * charWidth,
           |        y = n / perLine * charHeight,
           |        width = charWidth,
           |        height = charHeight
           |      )
           |    }
           |
           |    FontInfo(fontKey, Size(width, height), fontChar(63), Batch.fromSeq((startAt to endAt).map(fontChar)), caseSensitive = true)
           |""".stripMargin
      )

Short of doing real GPU text rendering, we could generate font sheets + info in the plugin based on a TTF. Producing them for fixed sizes is perfectly ok.

The road to GPU font's might be:

  1. As above.
  2. Alter to read glyph info output to code, and have indigo do something like the above onload (requires render targets?). Since we only need pixel art, this may not be too bad (if pixel inside glphy then render else dispose)
  3. Instead of rendering immediately, use the glyph info to render SDF data.
davesmith00000 commented 5 months ago

Will be in the next release.