diff --git a/.idea/artifacts/TerrarumBuild.xml b/.idea/artifacts/TerrarumBuild.xml index 906f21916..614d8a898 100644 --- a/.idea/artifacts/TerrarumBuild.xml +++ b/.idea/artifacts/TerrarumBuild.xml @@ -30,13 +30,7 @@ - - - - - - @@ -91,6 +85,9 @@ + + + \ No newline at end of file diff --git a/.idea/libraries/KotlinJavaRuntime.xml b/.idea/libraries/KotlinJavaRuntime.xml index a899a6e5e..409da39e6 100644 --- a/.idea/libraries/KotlinJavaRuntime.xml +++ b/.idea/libraries/KotlinJavaRuntime.xml @@ -1,26 +1,23 @@ - + - - - + + - + - - - + + - + - - - + + - + \ No newline at end of file diff --git a/lib/TerrarumSansBitmap.jar b/lib/TerrarumSansBitmap.jar index 37f803a3b..c9a0f2eab 100644 --- a/lib/TerrarumSansBitmap.jar +++ b/lib/TerrarumSansBitmap.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:392ec047781e4fb3d2a613200996a27e0c4f23db2df351f15d3cc476350f34d1 -size 203098 +oid sha256:a51b4b41e0c1e28f49e5ccac1e6629c0d08377d5e6840581505f78d02b2fc366 +size 205412 diff --git a/src/net/torvald/btex/BTeXDocument.kt b/src/net/torvald/btex/BTeXDocument.kt index e7379128c..eb203a4a4 100644 --- a/src/net/torvald/btex/BTeXDocument.kt +++ b/src/net/torvald/btex/BTeXDocument.kt @@ -5,14 +5,15 @@ import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.TextureRegion -import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.utils.Disposable -import net.torvald.terrarum.* +import net.torvald.terrarum.FlippingSpriteBatch +import net.torvald.terrarum.ceilToInt import net.torvald.terrarum.imagefont.TinyAlphNum import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustfile import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClustfileOutputStream import net.torvald.terrarum.serialise.Common +import net.torvald.terrarum.tryDispose import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.utils.JsonFetcher import net.torvald.terrarumsansbitmap.MovableType @@ -57,6 +58,7 @@ class BTeXDocument : Disposable { val DEFAULT_PAGE_BACK = Color(0xe0dfdb_ff.toInt()) // val DEFAULT_PAGE_FORE = Color(0x0a0706_ff) val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff) + val ccPagenum = TerrarumSansBitmap.toColorCode(0xf333) private fun String.escape() = this.replace("\"", "\\\"") @@ -84,7 +86,7 @@ class BTeXDocument : Disposable { doc.inner = metaJson["inner"].asString() doc.papersize = metaJson["papersize"].asString() doc.fromArchive = true - doc.pageTextures = ArrayList() + doc.pageTextures = Array(pageCount) { null } println("Title: ${doc.theTitle}") @@ -97,7 +99,7 @@ class BTeXDocument : Disposable { val tempFile = Gdx.files.external("./.btex-import.png") // must create new file descriptor for every page, or else every page will share a single file descriptor which cause problems it.exportFileTo(tempFile.file()) val texture = TextureRegion(Texture(tempFile)) - doc.pageTextures.add(texture) + doc.pageTextures[page] = texture if (page == 0) { doc.textWidth = texture.regionWidth - 2 * doc.pageMarginH @@ -117,8 +119,8 @@ class BTeXDocument : Disposable { internal val pages = ArrayList() - private lateinit var pageTextures: ArrayList - private lateinit var pageFrameBuffers: ArrayList + private lateinit var pagePixmaps: Array + private lateinit var pageTextures: Array val currentPage: Int get() = pages.size - 1 @@ -143,53 +145,44 @@ class BTeXDocument : Disposable { linesPrintedOnPage.add(index, 0) } + private val lock = Any() + private val texturefiedPages = HashSet() + /** * Must be called on a thread with GL context! */ - fun finalise() { - if (fromArchive) throw IllegalStateException("Document is loaded from the archive and thus cannot be finalised") - if (isFinalised) throw IllegalStateException("Page is already been finalised") + fun finalise(multithread: Boolean = false) { + synchronized(lock) { + if (fromArchive) throw IllegalStateException("Document is loaded from the archive and thus cannot be finalised") + if (isFinalised) throw IllegalStateException("Page is already been finalised") - // TODO serialise and finalise via CPU (store every page as Pixmap) + // serialise and finalise via CPU (store every page as Pixmap) - pageTextures = ArrayList() - pageFrameBuffers = ArrayList() + pageTextures = Array(pages.size) { null } + pagePixmaps = Array(pages.size) { null } - val camera = OrthographicCamera(pageDimensionWidth.toFloat(), pageDimensionHeight.toFloat()) - val batch = FlippingSpriteBatch() - - pages.forEachIndexed { pageNum, page -> - val fbo = FrameBuffer(Pixmap.Format.RGBA8888, pageDimensionWidth, pageDimensionHeight, false) - fbo.inAction(null, null) { - - camera.setToOrtho(false, pageDimensionWidth.toFloat(), pageDimensionHeight.toFloat()) - camera.position?.set((pageDimensionWidth / 2f).roundToFloat(), (pageDimensionHeight / 2f).roundToFloat(), 0f) // TODO floor? ceil? round? - camera.update() - batch.projectionMatrix = camera.combined - - - blendNormalStraightAlpha(batch) - batch.inUse { - page.render(0f, batch, 0, 0, pageMarginH, pageMarginV) - printPageNumber(batch, pageNum, 0, 0) + pages.forEachIndexed { pageNum, page -> + val pixmap = Pixmap(pageDimensionWidth, pageDimensionHeight, Pixmap.Format.RGBA8888).also { + it.blending = Pixmap.Blending.SourceOver + it.filter = Pixmap.Filter.NearestNeighbour } + page.renderToPixmap(pixmap, 0, 0, pageMarginH, pageMarginV) + printPageNumber(pixmap, pageNum, 0, 0) + pagePixmaps[pageNum] = pixmap } - pageTextures.add(TextureRegion(fbo.colorBufferTexture)) - pageFrameBuffers.add(fbo) + isFinalised = true } - isFinalised = true - - batch.dispose() } override fun dispose() { if (isFinalised) { - pageTextures.forEach { it.texture.dispose() } - pageFrameBuffers.forEach { it.dispose() } + pageTextures.forEach { it?.texture?.dispose() } + pagePixmaps.forEach { it?.tryDispose() } } else if (fromArchive) { - pageTextures.forEach { it.texture.dispose() } + pageTextures.forEach { it?.texture?.dispose() } + pagePixmaps.forEach { it?.tryDispose() } } } @@ -218,13 +211,9 @@ class BTeXDocument : Disposable { it.writeBytes(json.encodeToByteArray()) } - pageFrameBuffers.forEachIndexed { index, fbo -> - val file = Clustfile(DOM, "$index.png").also { - it.createNewFile() - } - - fbo.inAction(null, null) { - val pixmap = Pixmap.createFromFrameBuffer(0, 0, fbo.width, fbo.height) + pagePixmaps.forEachIndexed { index, pixmap -> + Clustfile(DOM, "$index.png").also { file -> + file.createNewFile() val tempFile = Gdx.files.external("./.btex-export.png") PixmapIO.writePNG(tempFile, pixmap, Deflater.BEST_COMPRESSION, false) val outstream = ClustfileOutputStream(file) @@ -262,8 +251,13 @@ class BTeXDocument : Disposable { fun render(frameDelta: Float, batch: SpriteBatch, page: Int, x: Int, y: Int) { batch.color = Color.WHITE - if (isFinalised || fromArchive) + if (fromArchive || isFinalised && texturefiedPages.contains(page)) batch.draw(pageTextures[page], x.toFloat(), y.toFloat()) + else if (isFinalised && !texturefiedPages.contains(page)) { + pageTextures[page] = TextureRegion(Texture(pagePixmaps[page])) + texturefiedPages.add(page) + batch.draw(pageTextures[page], x.toFloat(), y.toFloat()) + } else { pages[page].render(frameDelta, batch, x, y, pageMarginH, pageMarginV) printPageNumber(batch, page, x, y) @@ -289,6 +283,26 @@ class BTeXDocument : Disposable { TinyAlphNum.draw(batch, num, numX.toFloat(), numY.toFloat()) } } + + private fun printPageNumber(pixmap: Pixmap, page: Int, x: Int, y: Int) { + val num = "${page + 1}" + val numW = TinyAlphNum.getWidth(num) + val numX = if (context == "tome") { + if (page % 2 == 1) + x + pageMarginH + else + x + pageDimensionWidth - pageMarginH - numW + } + else { + x + (pageDimensionWidth - numW) / 2 + } + val numY = y + pageDimensionHeight - 2 * pageMarginV - 4 + + if (page == 0 && context != "tome" || page in tocPageStart until endOfPageStart) { + pixmap.setColor(DEFAULT_ORNAMENTS_COL) + TinyAlphNum.drawToPixmap(pixmap, "$ccPagenum$num", numX, numY) + } + } } class BTeXPage( @@ -320,6 +334,18 @@ class BTeXPage( fun isEmpty() = drawCalls.isEmpty() fun isNotEmpty() = drawCalls.isNotEmpty() + fun renderToPixmap(pixmap: Pixmap, x: Int, y: Int, marginH: Int, marginV: Int) { + drawCalls.sortedBy { if (it.text != null) 16 else 0 }.let { drawCalls -> + val backCol = back.cpy().also { it.a = 0.93f } + pixmap.setColor(backCol) + pixmap.fill() + + pixmap.setColor(Color.WHITE) + drawCalls.forEach { + it.drawToPixmap(pixmap, x + marginH, y + marginV) + } + } + } } @@ -328,6 +354,10 @@ data class TypesetDrawCall(val movableType: MovableType, val rowStart: Int, val fun draw(doc: BTeXDocument, batch: SpriteBatch, x: Float, y: Float) { movableType.draw(batch, x, y, rowStart, minOf(rows, doc.pageLines)) } + + fun drawToPixmap(doc: BTeXDocument, pixmap: Pixmap, x: Int, y: Int) { + movableType.drawToPixmap(pixmap, x, y, rowStart, minOf(rows, doc.pageLines)) + } } abstract class BTeXBatchDrawCall( @@ -336,6 +366,7 @@ abstract class BTeXBatchDrawCall( val parentText: BTeXDrawCall?// = null ) { abstract fun draw(doc: BTeXDocument, batch: SpriteBatch, x: Float, y: Float, font: TerrarumSansBitmap? = null) + abstract fun drawToPixmap(doc: BTeXDocument, pixmap: Pixmap, x: Int, y: Int, font: TerrarumSansBitmap? = null) } class BTeXDrawCall( @@ -359,11 +390,6 @@ class BTeXDrawCall( val px = (posX + x).toFloat() val py = (posY + y).toFloat() - if (theme == "code") { - // todo draw code background - println("code themed") - } - extraDrawFun(batch, px, py) batch.color = Color.WHITE @@ -384,6 +410,23 @@ class BTeXDrawCall( return true } + fun drawToPixmap(pixmap: Pixmap, x: Int, y: Int) { + val px = posX + x + val py = posY + y + + extraPixmapDrawFun(pixmap, px, py) + + pixmap.setColor(Color.WHITE) + + if (text != null && cmd == null) { + text.drawToPixmap(doc, pixmap, px, py) + } + else if (text == null && cmd != null) { + cmd.drawToPixmap(doc, pixmap, px, py, font) + } + else throw Error("Text and Texture are both non-null") + } + internal val width: Int get() = if (text != null) text.movableType.width * text.movableType.font.scale @@ -391,6 +434,7 @@ class BTeXDrawCall( cmd!!.width internal var extraDrawFun: (SpriteBatch, Float, Float) -> Unit = { _, _, _ ->} + internal var extraPixmapDrawFun: (Pixmap, Int, Int) -> Unit = { _, _, _ ->} internal val lineCount = if (text != null) text.rows else diff --git a/src/net/torvald/btex/BTeXParser.kt b/src/net/torvald/btex/BTeXParser.kt index 2cf65467e..955ff791a 100644 --- a/src/net/torvald/btex/BTeXParser.kt +++ b/src/net/torvald/btex/BTeXParser.kt @@ -2,7 +2,9 @@ package net.torvald.btex import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.utils.Disposable import com.jme3.math.FastMath.DEG_TO_RAD import net.torvald.colourutil.OKLch import net.torvald.colourutil.tosRGB @@ -121,13 +123,6 @@ object BTeXParser { private var hasCover = false private var coverCol: Color? = null - private lateinit var testFont: TerrarumSansBitmap - private lateinit var partTitleFont: TerrarumSansBitmap - private lateinit var titleFont: TerrarumSansBitmap - private lateinit var subtitleFont: TerrarumSansBitmap - - private val bodyTextShadowAlpha = 0.36f - private val macrodefs = hashMapOf( "thepart" to "Part %1\$s", "parttype" to "I", @@ -158,6 +153,10 @@ object BTeXParser { } init { + if (!fontInit) { + throw RuntimeException("Font not initialised: call BTeXParser.BTeXHandler.preloadFonts() WITHIN OpenGL-context thread to initialise the fonts.") + } + BTeXHandler::class.declaredFunctions.filter { it.findAnnotation() != null }.forEach { // println("Tag opener: ${it.name}") elemOpeners[it.name] = it @@ -179,6 +178,14 @@ object BTeXParser { font.draw(batch, "${ccDefault}E", x + (15 + 2*interchar)*scale, y + 4*scale) font.draw(batch, "${ccDefault}X", x + (23 + 3*interchar)*scale, y + 0*scale) } + override fun drawToPixmap(doc: BTeXDocument, pixmap: Pixmap, x: Int, y: Int, font: TerrarumSansBitmap?) { + val scale = font!!.scale + val interchar = font.interchar + font.drawToPixmap(pixmap, "${ccDefault}B", x + ( 0 + 0*interchar)*scale, y + 0*scale) + font.drawToPixmap(pixmap, "${ccDefault}T", x + ( 8 + 1*interchar)*scale, y + 0*scale) + font.drawToPixmap(pixmap, "${ccDefault}E", x + (15 + 2*interchar)*scale, y + 4*scale) + font.drawToPixmap(pixmap, "${ccDefault}X", x + (23 + 3*interchar)*scale, y + 0*scale) + } } } @@ -194,6 +201,15 @@ object BTeXParser { font.draw(batch, "${ccDefault}E", x + (19 + 2 * interchar) * scale, y + 4 * scale) font.draw(batch, "${ccDefault}X", x + (27 + 3 * interchar) * scale, y + 0 * scale) } + override fun drawToPixmap(doc: BTeXDocument, pixmap: Pixmap, x: Int, y: Int, font: TerrarumSansBitmap?) { + val scale = font!!.scale + val interchar = font.interchar + font.drawToPixmap(pixmap, "${ccDefault}L", x + (0 + 0 * interchar) * scale, y + 0 * scale) + font.drawToPixmap(pixmap, "${ccDefault}ᴀ", x + (4 + 0 * interchar) * scale, y + -4 * scale) + font.drawToPixmap(pixmap, "${ccDefault}T", x + (12 + 1 * interchar) * scale, y + 0 * scale) + font.drawToPixmap(pixmap, "${ccDefault}E", x + (19 + 2 * interchar) * scale, y + 4 * scale) + font.drawToPixmap(pixmap, "${ccDefault}X", x + (27 + 3 * interchar) * scale, y + 0 * scale) + } } } @@ -207,17 +223,17 @@ object BTeXParser { font.draw(batch, "${ccDefault}E", x + (7 + 2 * interchar) * scale, y + 4 * scale) font.draw(batch, "${ccDefault}X", x + (15 + 3 * interchar) * scale, y + 0 * scale) } + override fun drawToPixmap(doc: BTeXDocument, pixmap: Pixmap, x: Int, y: Int, font: TerrarumSansBitmap?) { + val scale = font!!.scale + val interchar = font.interchar + font.drawToPixmap(pixmap, "${ccDefault}T", x + (0 + 1 * interchar) * scale, y + 0 * scale) + font.drawToPixmap(pixmap, "${ccDefault}E", x + (7 + 2 * interchar) * scale, y + 4 * scale) + font.drawToPixmap(pixmap, "${ccDefault}X", x + (15 + 3 * interchar) * scale, y + 0 * scale) + } } } } - fun dispose() { - if (::testFont.isInitialized) testFont.tryDispose() - if (::titleFont.isInitialized) titleFont.tryDispose() - if (::partTitleFont.isInitialized) partTitleFont.tryDispose() - if (::subtitleFont.isInitialized) subtitleFont.tryDispose() - } - override fun warning(e: SAXParseException) { e.printStackTrace() } @@ -357,31 +373,18 @@ object BTeXParser { private fun getFont() = when (cover) { "typewriter" -> TODO() - else -> { - if (!::testFont.isInitialized) testFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha, textCacheSize = 4096) - testFont - } + else -> testFont } private fun getPartTitleFont(): TerrarumSansBitmap { - if (!::partTitleFont.isInitialized) partTitleFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha).also { - it.interchar = 1 - } return partTitleFont } private fun getTitleFont(): TerrarumSansBitmap { - if (!::titleFont.isInitialized) titleFont = TerrarumSansBitmap(App.FONT_DIR).also { - it.interchar = 1 - it.scale = 2 - } return titleFont } private fun getSubtitleFont(): TerrarumSansBitmap { - if (!::subtitleFont.isInitialized) subtitleFont = TerrarumSansBitmap(App.FONT_DIR).also { - it.interchar = 1 - } return subtitleFont } @@ -1020,6 +1023,27 @@ object BTeXParser { batch.color = oldcol } } + it.extraPixmapDrawFun = { pixmap, x, y -> + val width = doc.textWidth - 2 * MARGIN_PARBOX_H + val height = it.lineCount * doc.lineHeightInPx + + if (height > 0) { + pixmap.setColor(Color(0xccccccff.toInt())) + pixmap.fillRectangle( + x - MARGIN_PARBOX_H, + y - MARGIN_PARBOX_V, + width + 2 * MARGIN_PARBOX_H, + height + 2 * MARGIN_PARBOX_V + ) + pixmap.setColor(Color(0x999999ff.toInt())) + Toolkit.drawBoxBorderToPixmap(pixmap, + x - MARGIN_PARBOX_H, + y - MARGIN_PARBOX_V, + width + 2 * MARGIN_PARBOX_H, + height + 2 * MARGIN_PARBOX_V + ) + } + } } insertOneEmptyLineOrAddNewPage() @@ -1291,6 +1315,15 @@ object BTeXParser { batch.color = Color.WHITE Toolkit.fillArea(batch, px, py, pw, 1f) } + it.last().extraPixmapDrawFun = { pixmap, x, y -> + val px = x + val py = y + doc.lineHeightInPx - 1 + val pw = doc.textWidth - 2 * MARGIN_TITLE_TEXTS + pixmap.setColor(Color(1f,1f,1f,.5f)) + pixmap.fillRectangle(px, py, pw+1, 2) + pixmap.setColor(Color.WHITE) + pixmap.fillRectangle(px, py, pw, 1) + } it.forEach { it.posX += MARGIN_TITLE_TEXTS @@ -1389,6 +1422,22 @@ object BTeXParser { ) batch.color = oldCol } + it.extraPixmapDrawFun = { pixmap, x, y -> + pixmap.setColor(DEFAULT_ORNAMENTS_COL.cpy().also { it.a *= bodyTextShadowAlpha }) + pixmap.fillRectangle( + x - (indent - 2), + y + doc.lineHeightInPx, + 7, + 1 + (it.lineCount - 1).coerceAtLeast(1) * doc.lineHeightInPx + ) + pixmap.setColor(DEFAULT_ORNAMENTS_COL) + pixmap.fillRectangle( + x - (indent - 2), + y + doc.lineHeightInPx, + 6, + (it.lineCount - 1).coerceAtLeast(1) * doc.lineHeightInPx + ) + } } } } @@ -1601,11 +1650,25 @@ object BTeXParser { font.draw(batch, pageNum, x + typeWidth - pageNumWidth.toFloat(), y) batch.color = oldCol - - - // println("pos: ($x, $y)\tTOC: $name -- dot start: ${(x + textWidth).div(dotGap).ceilToFloat() * dotGap}, dot end: $dotCursor, typeWidth=$typeWidth, pageNumWidth=$pageNumWidth") } + call.extraPixmapDrawFun = { pixmap, x, y -> + val font = getFont() + val y = y + (call.lineCount - 1).coerceAtLeast(0) * doc.lineHeightInPx + + val textWidth = if (call.text is TypesetDrawCall) { + font.getWidthNormalised(call.text.movableType.typesettedSlugs.last()) + } + else call.width + + var dotCursor = (x.toFloat() + textWidth).div(dotGap).ceilToInt() * dotGap + while (dotCursor < x + dotPosEnd) { + font.drawToPixmap(pixmap, "$ccDefault·", dotCursor + dotGap/2, y) + dotCursor += dotGap + } + + font.drawToPixmap(pixmap, "$ccDefault$pageNum", x + typeWidth - pageNumWidth, y) + } } } } @@ -1619,10 +1682,44 @@ object BTeXParser { companion object { + init { + App.disposables.add(object : Disposable { + override fun dispose() { + testFont.dispose() + partTitleFont.dispose() + titleFont.dispose() + subtitleFont.dispose() + } + }) + } + private val siblingAwareTags = arrayOf( "PART","CHAPTER","SECTION","SUBSECTION","P","I","LI" ) + private val bodyTextShadowAlpha = 0.36f + + private var fontInit = false + private lateinit var testFont: TerrarumSansBitmap + private lateinit var partTitleFont: TerrarumSansBitmap + private lateinit var titleFont: TerrarumSansBitmap + private lateinit var subtitleFont: TerrarumSansBitmap + + fun preloadFonts() { + testFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha, textCacheSize = 4096) + partTitleFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha).also { + it.interchar = 1 + } + titleFont = TerrarumSansBitmap(App.FONT_DIR).also { + it.interchar = 1 + it.scale = 2 + } + subtitleFont = TerrarumSansBitmap(App.FONT_DIR).also { + it.interchar = 1 + } + fontInit = true + } + private const val MARGIN_PARBOX_V = 4 private const val MARGIN_PARBOX_H = 12 private const val MARGIN_TITLE_TEXTS = 8 diff --git a/src/net/torvald/terrarum/imagefont/TinyAlphNum.kt b/src/net/torvald/terrarum/imagefont/TinyAlphNum.kt index f595b2382..73eb86ab1 100644 --- a/src/net/torvald/terrarum/imagefont/TinyAlphNum.kt +++ b/src/net/torvald/terrarum/imagefont/TinyAlphNum.kt @@ -1,11 +1,14 @@ package net.torvald.terrarum.imagefont +import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.g2d.Batch import com.badlogic.gdx.graphics.g2d.BitmapFont import com.badlogic.gdx.graphics.g2d.GlyphLayout import net.torvald.terrarum.roundToFloat import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack +import kotlin.math.roundToInt /** * Created by minjaesong on 2016-04-15. @@ -16,13 +19,18 @@ object TinyAlphNum : BitmapFont() { internal const val H = 13 internal val fontSheet = TextureRegionPack("./assets/graphics/fonts/7x13_Tamzen7x14b.tga", W+1, H+1) - + internal val fontPixmap = Pixmap(Gdx.files.internal("./assets/graphics/fonts/7x13_Tamzen7x14b.tga")) init { setOwnsTexture(true) setUseIntegerPositions(true) } + override fun dispose() { + fontSheet.dispose() + fontPixmap.dispose() + } + fun getWidth(str: String): Int { var l = 0 for (char in str) { @@ -33,7 +41,8 @@ object TinyAlphNum : BitmapFont() { return W * l } - lateinit var colMain: Color + private var colMain = Color.WHITE + private var colMainInt = -1 override fun draw(batch: Batch, text: CharSequence, x: Float, y: Float): GlyphLayout? { val originalColour = batch.color.cpy() @@ -65,6 +74,79 @@ object TinyAlphNum : BitmapFont() { return null } + fun drawToPixmap(pixmap: Pixmap, text: String, x: Int, y: Int) { + var charsPrinted = 0 + + + text.forEachIndexed { index, c -> + if (isColourCodeHigh(c)) { + val cchigh = c + val cclow = text[index + 1] + val colour = getColour(cchigh, cclow) + + colMainInt = colour.toRGBA8888() + } + else if (c in 0.toChar()..255.toChar()) { + val srcX = (c.code % 16) * (W+1) + val srcY = (c.code / 16) * (H+1) + val destX = x + charsPrinted * W + val destY = y + + pixmap.drawPixmap(fontPixmap, srcX, srcY, W+1, H+1, destX, destY, colMainInt) + + charsPrinted += 1 + } + } + } + + + /*** + * @param col RGBA8888 representation + */ + private fun Pixmap.drawPixmap(pixmap: Pixmap, srcX: Int, srcY: Int, srcW: Int, srcH: Int, destX: Int, destY: Int, col: Int) { + for (y in srcY until srcY + srcH) { + for (x in srcX until srcX + srcW) { + val pixel = pixmap.getPixel(x, y) + + val newPixel = pixel colorTimes col + + this.drawPixel(destX + x - srcX, destY + y - srcY, newPixel) + } + } + } + + private fun Color.toRGBA8888() = + (this.r * 255f).toInt().shl(24) or + (this.g * 255f).toInt().shl(16) or + (this.b * 255f).toInt().shl(8) or + (this.a * 255f).toInt() + + /** + * RGBA8888 representation + */ + private fun Int.forceOpaque() = this.and(0xFFFFFF00.toInt()) or 0xFF + + private infix fun Int.colorTimes(other: Int): Int { + val thisBytes = IntArray(4) { this.ushr(it * 8).and(255) } + val otherBytes = IntArray(4) { other.ushr(it * 8).and(255) } + + return (thisBytes[0] times256 otherBytes[0]) or + (thisBytes[1] times256 otherBytes[1]).shl(8) or + (thisBytes[2] times256 otherBytes[2]).shl(16) or + (thisBytes[3] times256 otherBytes[3]).shl(24) + } + + private infix fun Int.times256(other: Int) = multTable255[this][other] + + private val multTable255 = Array(256) { left -> + IntArray(256) { right -> + (255f * (left / 255f).times(right / 255f)).roundToInt() + } + } + + + + override fun getLineHeight() = H.toFloat() override fun getCapHeight() = getLineHeight() override fun getXHeight() = getLineHeight() diff --git a/src/net/torvald/terrarum/tests/BTeXTest.kt b/src/net/torvald/terrarum/tests/BTeXTest.kt index d6529c68f..557877ba0 100644 --- a/src/net/torvald/terrarum/tests/BTeXTest.kt +++ b/src/net/torvald/terrarum/tests/BTeXTest.kt @@ -13,7 +13,9 @@ import com.badlogic.gdx.graphics.glutils.ShaderProgram import net.torvald.btex.BTeXParser import net.torvald.terrarum.* import net.torvald.terrarum.btex.BTeXDocument +import net.torvald.terrarum.imagefont.TinyAlphNum import net.torvald.terrarum.langpack.Lang +import net.torvald.terrarum.ui.Toolkit import net.torvald.unicode.EMDASH import java.io.File import kotlin.system.measureTimeMillis @@ -44,7 +46,10 @@ class BTeXTest : ApplicationAdapter() { ) override fun create() { - Lang.invoke() + Lang + TinyAlphNum + Toolkit + BTeXParser.BTeXHandler.preloadFonts() batch = FlippingSpriteBatch(1000) camera = OrthographicCamera(1280f, 720f) @@ -57,26 +62,27 @@ class BTeXTest : ApplicationAdapter() { val isBookFinalised = filePath.endsWith(".btxbook") if (!isBookFinalised) { - measureTimeMillis { - val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"), varMap) - document = f.first - documentHandler = f.second - }.also { - println("Time spent on typesetting [ms]: $it") - } + Thread { + measureTimeMillis { + val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"), varMap) + document = f.first + documentHandler = f.second + }.also { + println("Time spent on typesetting [ms]: $it") + } - /*measureTimeMillis { - document.finalise() - documentHandler.dispose() - }.also { - println("Time spent on finalising [ms]: $it") - } + measureTimeMillis { + document.finalise() + }.also { + println("Time spent on finalising [ms]: $it") + } - measureTimeMillis { + /*measureTimeMillis { document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btxbook")}")) - }.also { - println("Time spent on serialisation [ms]: $it") - }*/ + }.also { + println("Time spent on serialisation [ms]: $it") + }*/ + }.start() } else { measureTimeMillis { @@ -96,28 +102,35 @@ class BTeXTest : ApplicationAdapter() { gdxClearAndEnableBlend(.063f, .070f, .086f, 1f) - val drawX = (1280 - (pageGap + document.pageDimensionWidth*2)) / 2 - val drawY = 24 + if (::document.isInitialized) { + if (document.isFinalised) { + val drawX = (1280 - (pageGap + document.pageDimensionWidth * 2)) / 2 + val drawY = 24 - batch.inUse { - batch.color = Color.WHITE - batch.draw(bg, 0f, 0f) + batch.inUse { + batch.color = Color.WHITE + batch.draw(bg, 0f, 0f) - if (scroll - 1 in document.pageIndices) - document.render(0f, batch, scroll - 1, drawX, drawY) - if (scroll in document.pageIndices) - document.render(0f, batch, scroll, drawX + (6 + document.pageDimensionWidth), drawY) + if (scroll - 1 in document.pageIndices) + document.render(0f, batch, scroll - 1, drawX, drawY) + if (scroll in document.pageIndices) + document.render(0f, batch, scroll, drawX + (6 + document.pageDimensionWidth), drawY) + } + + + if (Gdx.input.isKeyJustPressed(Input.Keys.LEFT)) + scroll = (scroll - 2).coerceAtLeast(0) + else if (Gdx.input.isKeyJustPressed(Input.Keys.RIGHT)) + scroll = + (scroll + 2).coerceAtMost( + document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2) + ) + else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_UP)) + scroll = 0 + else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_DOWN)) + scroll = document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2) + } } - - - if (Gdx.input.isKeyJustPressed(Input.Keys.LEFT)) - scroll = (scroll - 2).coerceAtLeast(0) - else if (Gdx.input.isKeyJustPressed(Input.Keys.RIGHT)) - scroll = (scroll + 2).coerceAtMost(document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2)) - else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_UP)) - scroll = 0 - else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_DOWN)) - scroll = document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2) } diff --git a/src/net/torvald/terrarum/ui/Toolkit.kt b/src/net/torvald/terrarum/ui/Toolkit.kt index b96397eff..fd885e778 100644 --- a/src/net/torvald/terrarum/ui/Toolkit.kt +++ b/src/net/torvald/terrarum/ui/Toolkit.kt @@ -188,6 +188,18 @@ object Toolkit : Disposable { } + // draws highly simplified box border + fun drawBoxBorderToPixmap(pixmap: Pixmap, x: Int, y: Int, w: Int, h: Int) { + // top edge + pixmap.fillRectangle(x, y - 1, w, 1) + // bottom edge + pixmap.fillRectangle(x, y + h, w, 1) + // left edge + pixmap.fillRectangle(x - 1, y, 1, h) + // right edge + pixmap.fillRectangle(x + w, y, 1, h) + } + private lateinit var blurtex0: Texture private lateinit var blurtex1: Texture private lateinit var blurtex2: Texture