mirror of https://github.com/irmen/ksim65.git
139 lines
6.2 KiB
Kotlin
139 lines
6.2 KiB
Kotlin
package razorvine.fonts
|
|
|
|
import java.awt.Color
|
|
import java.awt.Graphics2D
|
|
import java.awt.Transparency
|
|
import java.awt.image.BufferedImage
|
|
import java.io.File
|
|
import java.io.FileInputStream
|
|
import java.io.IOException
|
|
import java.util.zip.GZIPInputStream
|
|
|
|
|
|
class PsfFont(name: String) {
|
|
|
|
// font format info: https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
|
|
|
|
val numChars: Int
|
|
val bytesPerChar: Int
|
|
val height: Int
|
|
val width: Int
|
|
private val hasUnicodeTable: Boolean
|
|
private val rawBitmaps: List<ByteArray>
|
|
|
|
init {
|
|
val data: ByteArray
|
|
val fontsDirectory = "/usr/share/kbd/consolefonts"
|
|
var stream = javaClass.getResourceAsStream("/charset/$name.psfu.gz") ?:
|
|
javaClass.getResourceAsStream("/charset/$name.psf.gz") ?:
|
|
(if(File("$fontsDirectory/$name.psfu.gz").exists()) FileInputStream("$fontsDirectory/$name.psfu.gz") else null ) ?:
|
|
(if(File("$fontsDirectory/$name.psf.gz").exists()) FileInputStream("$fontsDirectory/$name.psf.gz") else null ) ?:
|
|
(if(File("$fontsDirectory/$name.fnt.gz").exists()) FileInputStream("$fontsDirectory/$name.fnt.gz") else null )
|
|
if(stream==null) {
|
|
stream = javaClass.getResourceAsStream("/charset/$name.psfu") ?:
|
|
javaClass.getResourceAsStream("/charset/$name.psf") ?:
|
|
(if(File("$fontsDirectory/$name.psfu").exists()) FileInputStream("$fontsDirectory/$name.psfu") else null ) ?:
|
|
(if(File("$fontsDirectory/$name.psf").exists()) FileInputStream("$fontsDirectory/$name.psf") else null ) ?:
|
|
(if(File("$fontsDirectory/$name.fnt").exists()) FileInputStream("$fontsDirectory/$name.fnt") else null ) ?:
|
|
throw IOException("no such font: $name")
|
|
data = stream.readBytes()
|
|
} else {
|
|
GZIPInputStream(stream).use { data = it.readBytes() }
|
|
}
|
|
stream.close()
|
|
|
|
if (data[0] == 0x36.toByte() && data[1] == 0x04.toByte()) {
|
|
// continue reading PSF1 font
|
|
val mode = data[2].toInt()
|
|
numChars = if (mode and 1 != 0) 512 else 256
|
|
bytesPerChar = data[3].toInt()
|
|
hasUnicodeTable = mode and 2 != 0
|
|
height = bytesPerChar
|
|
width = 8
|
|
rawBitmaps = (0..numChars).map {
|
|
data.sliceArray(3+it*bytesPerChar..3+(it+1)*bytesPerChar)
|
|
}
|
|
// ignore unicode table for now: val table = stream.readAllBytes()
|
|
} else {
|
|
if (data[0] == 0x72.toByte() && data[1] == 0xb5.toByte() && data[2] == 0x4a.toByte() && data[3] == 0x86.toByte()) {
|
|
// continue reading PSF2 font
|
|
// skip the version val version = makeInt(data, 4)
|
|
val headersize = makeInt(data, 8)
|
|
val flags = makeInt(data, 12)
|
|
hasUnicodeTable = flags and 1 != 0
|
|
numChars = makeInt(data, 16)
|
|
bytesPerChar = makeInt(data, 20)
|
|
height = makeInt(data, 24)
|
|
width = makeInt(data, 28)
|
|
rawBitmaps = (0..numChars).map {
|
|
data.sliceArray(headersize+it*bytesPerChar..headersize+(it+1)*bytesPerChar)
|
|
}
|
|
} else {
|
|
hasUnicodeTable = false
|
|
numChars = 0
|
|
bytesPerChar = 0
|
|
height = 0
|
|
width = 0
|
|
rawBitmaps = emptyList()
|
|
}
|
|
}
|
|
}
|
|
|
|
fun convertToImage(gfx: Graphics2D, textColor: Color): BufferedImage {
|
|
// create a single image with all the characters in a vertical column from top to bottom.
|
|
val bitmap = gfx.deviceConfiguration.createCompatibleImage((width+7) and 0b11111000, height*numChars, Transparency.BITMASK)
|
|
val bytesHoriz = (width+7)/8
|
|
val color = textColor.rgb
|
|
val nopixel = Color(0, 0, 0, 0).rgb
|
|
for (char in 0 until numChars) {
|
|
for (b in 0 until bytesPerChar) {
|
|
val c = rawBitmaps[char][b].toInt()
|
|
val ix = 8*(b%bytesHoriz)
|
|
val iy = b/bytesHoriz+char*height
|
|
bitmap.setRGB(ix, iy, if (c and 0b10000000 != 0) color else nopixel)
|
|
bitmap.setRGB(ix+1, iy, if (c and 0b01000000 != 0) color else nopixel)
|
|
bitmap.setRGB(ix+2, iy, if (c and 0b00100000 != 0) color else nopixel)
|
|
bitmap.setRGB(ix+3, iy, if (c and 0b00010000 != 0) color else nopixel)
|
|
bitmap.setRGB(ix+4, iy, if (c and 0b00001000 != 0) color else nopixel)
|
|
bitmap.setRGB(ix+5, iy, if (c and 0b00000100 != 0) color else nopixel)
|
|
bitmap.setRGB(ix+6, iy, if (c and 0b00000010 != 0) color else nopixel)
|
|
bitmap.setRGB(ix+7, iy, if (c and 0b00000001 != 0) color else nopixel)
|
|
}
|
|
}
|
|
return bitmap
|
|
}
|
|
|
|
private fun makeInt(bytes: ByteArray, offset: Int) =
|
|
makeInt(bytes[offset], bytes[offset+1], bytes[offset+2], bytes[offset+3])
|
|
private fun makeInt(b0: Byte, b1: Byte, b2: Byte, b3: Byte) =
|
|
b0.toInt() or (b1.toInt() shl 8) or (b2.toInt() shl 16) or (b3.toInt() shl 24)
|
|
}
|
|
|
|
|
|
|
|
// private fun loadFallbackCharacters(): Array<BufferedImage> {
|
|
// val img = ImageIO.read(javaClass.getResourceAsStream("/charset/unscii8x16.png"))
|
|
// val charactersImage = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
|
|
// charactersImage.createGraphics().drawImage(img, 0, 0, null)
|
|
//
|
|
// val black = Color(0, 0, 0).rgb
|
|
// val foreground = FG_COLOR.rgb
|
|
// val nopixel = Color(0, 0, 0, 0).rgb
|
|
// for (y in 0 until charactersImage.height) {
|
|
// for (x in 0 until charactersImage.width) {
|
|
// val col = charactersImage.getRGB(x, y)
|
|
// if (col == black) charactersImage.setRGB(x, y, nopixel)
|
|
// else charactersImage.setRGB(x, y, foreground)
|
|
// }
|
|
// }
|
|
//
|
|
// val numColumns = charactersImage.width/8
|
|
// val charImages = (0..255).map {
|
|
// val charX = it%numColumns
|
|
// val charY = it/numColumns
|
|
// charactersImage.getSubimage(charX*8, charY*16, 8, 16)
|
|
// }
|
|
//
|
|
// return charImages.toTypedArray()
|
|
// }
|