All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.ru.casperix.multiplatform.text.PixelFontBuilder.kt Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
package ru.casperix.multiplatform.text

import ru.casperix.multiplatform.font.FontReference
import ru.casperix.multiplatform.font.localize.CharacterSets
import ru.casperix.multiplatform.font.pixel.PixelFont
import ru.casperix.multiplatform.font.pixel.PixelFontSymbol
import ru.casperix.multiplatform.pixel_map.PixelMapRegion
import ru.casperix.multiplatform.loader.JvmImageConverter
import ru.casperix.math.vector.int32.Vector2i
import ru.casperix.renderer.material.*
import java.awt.*
import java.awt.font.FontRenderContext
import java.awt.font.GlyphVector
import java.awt.image.BufferedImage
import kotlin.math.roundToInt


@ExperimentalUnsignedTypes
object PixelFontBuilder {
    val antiAlias = true
    val bicubicInterpolation = true
    val subpixelRender = false

    class SymbolInfo(val char: Char, val size: Vector2i, val image: BufferedImage?)

    fun build(reference: FontReference, chars: Collection): PixelFont {
        val awtFont = Font(reference.name, Font.PLAIN, reference.size)
        val awtMetrics = createTempGraphic().getFontMetrics(awtFont)
        val metrics = getFontMetrics(awtFont)
        val baselineOffset = Vector2i(0,  - awtMetrics.ascent.toFloat().roundToInt())
        val config = TextureConfig(TextureFilter.NEAREST, TextureFilter.NEAREST, TextureWrap.CLAMP, TextureWrap.CLAMP, TextureWrap.CLAMP, false, false)

        val symbols = chars.mapNotNull { char ->
            getSymbolInfo(awtFont, char, awtMetrics)
        }.map {
            val graphic = if (it.image != null) {
                val pixelMap = JvmImageConverter.createPixelMap(it.image, reference.name + ":" + reference.size)
                PixelMapRegion(Texture2D( pixelMap, config))
            } else null

            PixelFontSymbol(it.char, baselineOffset, it.size, graphic)
        }

        return PixelFont(reference, metrics, symbols)
    }


    private fun createTempGraphic(): Graphics2D {
        /* Creating temporary image to extract character size */
        val tempImage = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
        val tempGraphic = tempImage.createGraphics()
        applyHints(tempGraphic)
        return tempGraphic
    }

    private fun getFontMetrics(font: Font): ru.casperix.multiplatform.font.FontMetrics {
        val tempGraphic = createTempGraphic()
        val awtMetrics = tempGraphic.getFontMetrics(font)


        val frc: FontRenderContext = tempGraphic.fontRenderContext
        val gv: GlyphVector = font.createGlyphVector(frc, CharacterSets.en.chars)
        val bounds = gv.getPixelBounds(null, 0f, 0f)

        val ascent = -bounds.y.toFloat()//awtMetrics.ascent.toFloat()
        val descent = awtMetrics.descent.toFloat()
        val leading = awtMetrics.leading.toFloat() + (awtMetrics.ascent.toFloat() - ascent)


        return ru.casperix.multiplatform.font.FontMetrics(ascent, descent, leading)
    }

    private fun applyHints(graphic: Graphics2D) = graphic.apply {
        if (antiAlias) {
            setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
        }
        if (bicubicInterpolation) {
            setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC)
        }
        setRenderingHint(
            RenderingHints.KEY_FRACTIONALMETRICS, if (subpixelRender) {
                RenderingHints.VALUE_FRACTIONALMETRICS_ON
            } else {
                RenderingHints.VALUE_FRACTIONALMETRICS_OFF
            }
        )
    }

    private fun getSymbolInfo(font: Font, c: Char, metrics: FontMetrics): SymbolInfo? {
        /* Get char charWidth and charHeight */
        val charWidth = metrics.charWidth(c)
        val charHeight = metrics.height
        val symbolSize = Vector2i(charWidth, charHeight)

        /* Check if charWidth is 0 */
        if (charWidth == 0 || charHeight == 0) {
            return null
        }

        if (!font.canDisplay(c)) {
            return SymbolInfo(c, symbolSize, null)
        }

        /* Create image for holding the char */
        val image = BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB)
        val graphic = image.createGraphics()
        applyHints(graphic)
        graphic.font = font
        graphic.paint = Color.WHITE

        graphic.drawString(c.toString(), 0, metrics.ascent)

//        graphic.paint = Color.RED
//        graphic.drawRect(0, 0, charWidth, charHeight)
//
//        val frc = FontRenderContext(AffineTransform(), true, false)
//        val vector = font.layoutGlyphVector(
//            frc,
//            charArrayOf( c),
//            0,
//            1,
//            Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT
//        )
//        graphic.drawGlyphVector(vector, 0f, metrics.ascent.toFloat())

        graphic.dispose()
        return SymbolInfo(c, symbolSize, image)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy