print book without GPU

This commit is contained in:
minjaesong
2024-05-16 01:35:58 +09:00
parent 1aecfc781f
commit 7320a14a4d
8 changed files with 383 additions and 141 deletions

View File

@@ -30,13 +30,7 @@
<element id="extracted-dir" path="$PROJECT_DIR$/lib/JTransforms-3.1.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/JTransforms-3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/JLargeArrays-1.5.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/JLargeArrays-1.5.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/aircompressor-0.25.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/aircompressor-0.25.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.22/kotlin-stdlib-jdk8-1.8.22.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.22/kotlin-stdlib-1.8.22.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.22/kotlin-stdlib-common-1.8.22.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" /> <element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.22/kotlin-stdlib-jdk7-1.8.22.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.22/kotlin-reflect-1.8.22.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-test/1.8.22/kotlin-test-1.8.22.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-1.12.1.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-1.12.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-backend-lwjgl3-1.12.1.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-backend-lwjgl3-1.12.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.3.3.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.3.3.jar" path-in-jar="/" />
@@ -91,6 +85,9 @@
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.12.1-natives-desktop.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.12.1-natives-desktop.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.12.1-natives-x86_64.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.12.1-natives-x86_64.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/TerranVirtualDisk.jar" path-in-jar="/" /> <element id="extracted-dir" path="$PROJECT_DIR$/lib/TerranVirtualDisk.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.24/kotlin-stdlib-jdk8-1.9.24.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.24/kotlin-stdlib-jdk7-1.9.24.jar" path-in-jar="/" />
</root> </root>
</artifact> </artifact>
</component> </component>

View File

@@ -1,26 +1,23 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository"> <library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22" /> <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.22/kotlin-stdlib-jdk8-1.8.22.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.24/kotlin-stdlib-jdk8-1.9.24.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.22/kotlin-stdlib-1.8.22.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.22/kotlin-stdlib-common-1.8.22.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.22/kotlin-stdlib-jdk7-1.8.22.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.24/kotlin-stdlib-jdk7-1.9.24.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.22/kotlin-stdlib-jdk8-1.8.22-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.24/kotlin-stdlib-jdk8-1.9.24-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.22/kotlin-stdlib-1.8.22-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.22/kotlin-stdlib-common-1.8.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.22/kotlin-stdlib-jdk7-1.8.22-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.24/kotlin-stdlib-jdk7-1.9.24-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.22/kotlin-stdlib-jdk8-1.8.22-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.24/kotlin-stdlib-jdk8-1.9.24-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.22/kotlin-stdlib-1.8.22-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.22/kotlin-stdlib-common-1.8.22-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.22/kotlin-stdlib-jdk7-1.8.22-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.24/kotlin-stdlib-jdk7-1.9.24-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
</component> </component>

Binary file not shown.

View File

@@ -5,14 +5,15 @@ import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.utils.Disposable 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.imagefont.TinyAlphNum
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM 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.Clustfile
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClustfileOutputStream import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClustfileOutputStream
import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.tryDispose
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.utils.JsonFetcher import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarumsansbitmap.MovableType import net.torvald.terrarumsansbitmap.MovableType
@@ -57,6 +58,7 @@ class BTeXDocument : Disposable {
val DEFAULT_PAGE_BACK = Color(0xe0dfdb_ff.toInt()) val DEFAULT_PAGE_BACK = Color(0xe0dfdb_ff.toInt())
// val DEFAULT_PAGE_FORE = Color(0x0a0706_ff) // val DEFAULT_PAGE_FORE = Color(0x0a0706_ff)
val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff) val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff)
val ccPagenum = TerrarumSansBitmap.toColorCode(0xf333)
private fun String.escape() = this.replace("\"", "\\\"") private fun String.escape() = this.replace("\"", "\\\"")
@@ -84,7 +86,7 @@ class BTeXDocument : Disposable {
doc.inner = metaJson["inner"].asString() doc.inner = metaJson["inner"].asString()
doc.papersize = metaJson["papersize"].asString() doc.papersize = metaJson["papersize"].asString()
doc.fromArchive = true doc.fromArchive = true
doc.pageTextures = ArrayList() doc.pageTextures = Array(pageCount) { null }
println("Title: ${doc.theTitle}") 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 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()) it.exportFileTo(tempFile.file())
val texture = TextureRegion(Texture(tempFile)) val texture = TextureRegion(Texture(tempFile))
doc.pageTextures.add(texture) doc.pageTextures[page] = texture
if (page == 0) { if (page == 0) {
doc.textWidth = texture.regionWidth - 2 * doc.pageMarginH doc.textWidth = texture.regionWidth - 2 * doc.pageMarginH
@@ -117,8 +119,8 @@ class BTeXDocument : Disposable {
internal val pages = ArrayList<BTeXPage>() internal val pages = ArrayList<BTeXPage>()
private lateinit var pageTextures: ArrayList<TextureRegion> private lateinit var pagePixmaps: Array<Pixmap?>
private lateinit var pageFrameBuffers: ArrayList<FrameBuffer> private lateinit var pageTextures: Array<TextureRegion?>
val currentPage: Int val currentPage: Int
get() = pages.size - 1 get() = pages.size - 1
@@ -143,53 +145,44 @@ class BTeXDocument : Disposable {
linesPrintedOnPage.add(index, 0) linesPrintedOnPage.add(index, 0)
} }
private val lock = Any()
private val texturefiedPages = HashSet<Int>()
/** /**
* Must be called on a thread with GL context! * Must be called on a thread with GL context!
*/ */
fun finalise() { fun finalise(multithread: Boolean = false) {
synchronized(lock) {
if (fromArchive) throw IllegalStateException("Document is loaded from the archive and thus cannot be finalised") if (fromArchive) throw IllegalStateException("Document is loaded from the archive and thus cannot be finalised")
if (isFinalised) throw IllegalStateException("Page is already been 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() pageTextures = Array(pages.size) { null }
pageFrameBuffers = ArrayList() pagePixmaps = Array(pages.size) { null }
val camera = OrthographicCamera(pageDimensionWidth.toFloat(), pageDimensionHeight.toFloat())
val batch = FlippingSpriteBatch()
pages.forEachIndexed { pageNum, page -> pages.forEachIndexed { pageNum, page ->
val fbo = FrameBuffer(Pixmap.Format.RGBA8888, pageDimensionWidth, pageDimensionHeight, false) val pixmap = Pixmap(pageDimensionWidth, pageDimensionHeight, Pixmap.Format.RGBA8888).also {
fbo.inAction(null, null) { it.blending = Pixmap.Blending.SourceOver
it.filter = Pixmap.Filter.NearestNeighbour
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)
} }
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() { override fun dispose() {
if (isFinalised) { if (isFinalised) {
pageTextures.forEach { it.texture.dispose() } pageTextures.forEach { it?.texture?.dispose() }
pageFrameBuffers.forEach { it.dispose() } pagePixmaps.forEach { it?.tryDispose() }
} }
else if (fromArchive) { 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()) it.writeBytes(json.encodeToByteArray())
} }
pageFrameBuffers.forEachIndexed { index, fbo -> pagePixmaps.forEachIndexed { index, pixmap ->
val file = Clustfile(DOM, "$index.png").also { Clustfile(DOM, "$index.png").also { file ->
it.createNewFile() file.createNewFile()
}
fbo.inAction(null, null) {
val pixmap = Pixmap.createFromFrameBuffer(0, 0, fbo.width, fbo.height)
val tempFile = Gdx.files.external("./.btex-export.png") val tempFile = Gdx.files.external("./.btex-export.png")
PixmapIO.writePNG(tempFile, pixmap, Deflater.BEST_COMPRESSION, false) PixmapIO.writePNG(tempFile, pixmap, Deflater.BEST_COMPRESSION, false)
val outstream = ClustfileOutputStream(file) val outstream = ClustfileOutputStream(file)
@@ -262,8 +251,13 @@ class BTeXDocument : Disposable {
fun render(frameDelta: Float, batch: SpriteBatch, page: Int, x: Int, y: Int) { fun render(frameDelta: Float, batch: SpriteBatch, page: Int, x: Int, y: Int) {
batch.color = Color.WHITE batch.color = Color.WHITE
if (isFinalised || fromArchive) if (fromArchive || isFinalised && texturefiedPages.contains(page))
batch.draw(pageTextures[page], x.toFloat(), y.toFloat()) 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 { else {
pages[page].render(frameDelta, batch, x, y, pageMarginH, pageMarginV) pages[page].render(frameDelta, batch, x, y, pageMarginH, pageMarginV)
printPageNumber(batch, page, x, y) printPageNumber(batch, page, x, y)
@@ -289,6 +283,26 @@ class BTeXDocument : Disposable {
TinyAlphNum.draw(batch, num, numX.toFloat(), numY.toFloat()) 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( class BTeXPage(
@@ -320,6 +334,18 @@ class BTeXPage(
fun isEmpty() = drawCalls.isEmpty() fun isEmpty() = drawCalls.isEmpty()
fun isNotEmpty() = drawCalls.isNotEmpty() 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) { fun draw(doc: BTeXDocument, batch: SpriteBatch, x: Float, y: Float) {
movableType.draw(batch, x, y, rowStart, minOf(rows, doc.pageLines)) 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( abstract class BTeXBatchDrawCall(
@@ -336,6 +366,7 @@ abstract class BTeXBatchDrawCall(
val parentText: BTeXDrawCall?// = null val parentText: BTeXDrawCall?// = null
) { ) {
abstract fun draw(doc: BTeXDocument, batch: SpriteBatch, x: Float, y: Float, font: TerrarumSansBitmap? = 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( class BTeXDrawCall(
@@ -359,11 +390,6 @@ class BTeXDrawCall(
val px = (posX + x).toFloat() val px = (posX + x).toFloat()
val py = (posY + y).toFloat() val py = (posY + y).toFloat()
if (theme == "code") {
// todo draw code background
println("code themed")
}
extraDrawFun(batch, px, py) extraDrawFun(batch, px, py)
batch.color = Color.WHITE batch.color = Color.WHITE
@@ -384,6 +410,23 @@ class BTeXDrawCall(
return true 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 internal val width: Int
get() = if (text != null) get() = if (text != null)
text.movableType.width * text.movableType.font.scale text.movableType.width * text.movableType.font.scale
@@ -391,6 +434,7 @@ class BTeXDrawCall(
cmd!!.width cmd!!.width
internal var extraDrawFun: (SpriteBatch, Float, Float) -> Unit = { _, _, _ ->} internal var extraDrawFun: (SpriteBatch, Float, Float) -> Unit = { _, _, _ ->}
internal var extraPixmapDrawFun: (Pixmap, Int, Int) -> Unit = { _, _, _ ->}
internal val lineCount = if (text != null) internal val lineCount = if (text != null)
text.rows text.rows
else else

View File

@@ -2,7 +2,9 @@ package net.torvald.btex
import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath.DEG_TO_RAD import com.jme3.math.FastMath.DEG_TO_RAD
import net.torvald.colourutil.OKLch import net.torvald.colourutil.OKLch
import net.torvald.colourutil.tosRGB import net.torvald.colourutil.tosRGB
@@ -121,13 +123,6 @@ object BTeXParser {
private var hasCover = false private var hasCover = false
private var coverCol: Color? = null 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( private val macrodefs = hashMapOf(
"thepart" to "Part %1\$s", "thepart" to "Part %1\$s",
"parttype" to "I", "parttype" to "I",
@@ -158,6 +153,10 @@ object BTeXParser {
} }
init { 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<OpenTag>() != null }.forEach { BTeXHandler::class.declaredFunctions.filter { it.findAnnotation<OpenTag>() != null }.forEach {
// println("Tag opener: ${it.name}") // println("Tag opener: ${it.name}")
elemOpeners[it.name] = it 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}E", x + (15 + 2*interchar)*scale, y + 4*scale)
font.draw(batch, "${ccDefault}X", x + (23 + 3*interchar)*scale, y + 0*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}E", x + (19 + 2 * interchar) * scale, y + 4 * scale)
font.draw(batch, "${ccDefault}X", x + (27 + 3 * interchar) * scale, y + 0 * 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,15 +223,15 @@ object BTeXParser {
font.draw(batch, "${ccDefault}E", x + (7 + 2 * interchar) * scale, y + 4 * scale) 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) 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) { override fun warning(e: SAXParseException) {
@@ -357,31 +373,18 @@ object BTeXParser {
private fun getFont() = when (cover) { private fun getFont() = when (cover) {
"typewriter" -> TODO() "typewriter" -> TODO()
else -> { else -> testFont
if (!::testFont.isInitialized) testFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha, textCacheSize = 4096)
testFont
}
} }
private fun getPartTitleFont(): TerrarumSansBitmap { private fun getPartTitleFont(): TerrarumSansBitmap {
if (!::partTitleFont.isInitialized) partTitleFont = TerrarumSansBitmap(App.FONT_DIR, shadowAlpha = bodyTextShadowAlpha).also {
it.interchar = 1
}
return partTitleFont return partTitleFont
} }
private fun getTitleFont(): TerrarumSansBitmap { private fun getTitleFont(): TerrarumSansBitmap {
if (!::titleFont.isInitialized) titleFont = TerrarumSansBitmap(App.FONT_DIR).also {
it.interchar = 1
it.scale = 2
}
return titleFont return titleFont
} }
private fun getSubtitleFont(): TerrarumSansBitmap { private fun getSubtitleFont(): TerrarumSansBitmap {
if (!::subtitleFont.isInitialized) subtitleFont = TerrarumSansBitmap(App.FONT_DIR).also {
it.interchar = 1
}
return subtitleFont return subtitleFont
} }
@@ -1020,6 +1023,27 @@ object BTeXParser {
batch.color = oldcol 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() insertOneEmptyLineOrAddNewPage()
@@ -1291,6 +1315,15 @@ object BTeXParser {
batch.color = Color.WHITE batch.color = Color.WHITE
Toolkit.fillArea(batch, px, py, pw, 1f) 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.forEach {
it.posX += MARGIN_TITLE_TEXTS it.posX += MARGIN_TITLE_TEXTS
@@ -1389,6 +1422,22 @@ object BTeXParser {
) )
batch.color = oldCol 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) font.draw(batch, pageNum, x + typeWidth - pageNumWidth.toFloat(), y)
batch.color = oldCol batch.color = oldCol
// println("pos: ($x, $y)\tTOC: $name -- dot start: ${(x + textWidth).div(dotGap).ceilToFloat() * dotGap}, dot end: $dotCursor, typeWidth=$typeWidth, pageNumWidth=$pageNumWidth") // 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 { companion object {
init {
App.disposables.add(object : Disposable {
override fun dispose() {
testFont.dispose()
partTitleFont.dispose()
titleFont.dispose()
subtitleFont.dispose()
}
})
}
private val siblingAwareTags = arrayOf( private val siblingAwareTags = arrayOf(
"PART","CHAPTER","SECTION","SUBSECTION","P","I","LI" "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_V = 4
private const val MARGIN_PARBOX_H = 12 private const val MARGIN_PARBOX_H = 12
private const val MARGIN_TITLE_TEXTS = 8 private const val MARGIN_TITLE_TEXTS = 8

View File

@@ -1,11 +1,14 @@
package net.torvald.terrarum.imagefont package net.torvald.terrarum.imagefont
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color 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.Batch
import com.badlogic.gdx.graphics.g2d.BitmapFont import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.GlyphLayout import com.badlogic.gdx.graphics.g2d.GlyphLayout
import net.torvald.terrarum.roundToFloat import net.torvald.terrarum.roundToFloat
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.roundToInt
/** /**
* Created by minjaesong on 2016-04-15. * Created by minjaesong on 2016-04-15.
@@ -16,13 +19,18 @@ object TinyAlphNum : BitmapFont() {
internal const val H = 13 internal const val H = 13
internal val fontSheet = TextureRegionPack("./assets/graphics/fonts/7x13_Tamzen7x14b.tga", W+1, H+1) 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 { init {
setOwnsTexture(true) setOwnsTexture(true)
setUseIntegerPositions(true) setUseIntegerPositions(true)
} }
override fun dispose() {
fontSheet.dispose()
fontPixmap.dispose()
}
fun getWidth(str: String): Int { fun getWidth(str: String): Int {
var l = 0 var l = 0
for (char in str) { for (char in str) {
@@ -33,7 +41,8 @@ object TinyAlphNum : BitmapFont() {
return W * l 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? { override fun draw(batch: Batch, text: CharSequence, x: Float, y: Float): GlyphLayout? {
val originalColour = batch.color.cpy() val originalColour = batch.color.cpy()
@@ -65,6 +74,79 @@ object TinyAlphNum : BitmapFont() {
return null 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 getLineHeight() = H.toFloat()
override fun getCapHeight() = getLineHeight() override fun getCapHeight() = getLineHeight()
override fun getXHeight() = getLineHeight() override fun getXHeight() = getLineHeight()

View File

@@ -13,7 +13,9 @@ import com.badlogic.gdx.graphics.glutils.ShaderProgram
import net.torvald.btex.BTeXParser import net.torvald.btex.BTeXParser
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.btex.BTeXDocument import net.torvald.terrarum.btex.BTeXDocument
import net.torvald.terrarum.imagefont.TinyAlphNum
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.Toolkit
import net.torvald.unicode.EMDASH import net.torvald.unicode.EMDASH
import java.io.File import java.io.File
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@@ -44,7 +46,10 @@ class BTeXTest : ApplicationAdapter() {
) )
override fun create() { override fun create() {
Lang.invoke() Lang
TinyAlphNum
Toolkit
BTeXParser.BTeXHandler.preloadFonts()
batch = FlippingSpriteBatch(1000) batch = FlippingSpriteBatch(1000)
camera = OrthographicCamera(1280f, 720f) camera = OrthographicCamera(1280f, 720f)
@@ -57,6 +62,7 @@ class BTeXTest : ApplicationAdapter() {
val isBookFinalised = filePath.endsWith(".btxbook") val isBookFinalised = filePath.endsWith(".btxbook")
if (!isBookFinalised) { if (!isBookFinalised) {
Thread {
measureTimeMillis { measureTimeMillis {
val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"), varMap) val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"), varMap)
document = f.first document = f.first
@@ -65,18 +71,18 @@ class BTeXTest : ApplicationAdapter() {
println("Time spent on typesetting [ms]: $it") println("Time spent on typesetting [ms]: $it")
} }
/*measureTimeMillis { measureTimeMillis {
document.finalise() document.finalise()
documentHandler.dispose()
}.also { }.also {
println("Time spent on finalising [ms]: $it") println("Time spent on finalising [ms]: $it")
} }
measureTimeMillis { /*measureTimeMillis {
document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btxbook")}")) document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btxbook")}"))
}.also { }.also {
println("Time spent on serialisation [ms]: $it") println("Time spent on serialisation [ms]: $it")
}*/ }*/
}.start()
} }
else { else {
measureTimeMillis { measureTimeMillis {
@@ -96,7 +102,9 @@ class BTeXTest : ApplicationAdapter() {
gdxClearAndEnableBlend(.063f, .070f, .086f, 1f) gdxClearAndEnableBlend(.063f, .070f, .086f, 1f)
val drawX = (1280 - (pageGap + document.pageDimensionWidth*2)) / 2 if (::document.isInitialized) {
if (document.isFinalised) {
val drawX = (1280 - (pageGap + document.pageDimensionWidth * 2)) / 2
val drawY = 24 val drawY = 24
batch.inUse { batch.inUse {
@@ -113,12 +121,17 @@ class BTeXTest : ApplicationAdapter() {
if (Gdx.input.isKeyJustPressed(Input.Keys.LEFT)) if (Gdx.input.isKeyJustPressed(Input.Keys.LEFT))
scroll = (scroll - 2).coerceAtLeast(0) scroll = (scroll - 2).coerceAtLeast(0)
else if (Gdx.input.isKeyJustPressed(Input.Keys.RIGHT)) else if (Gdx.input.isKeyJustPressed(Input.Keys.RIGHT))
scroll = (scroll + 2).coerceAtMost(document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2)) scroll =
(scroll + 2).coerceAtMost(
document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2)
)
else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_UP)) else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_UP))
scroll = 0 scroll = 0
else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_DOWN)) else if (Gdx.input.isKeyJustPressed(Input.Keys.PAGE_DOWN))
scroll = document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2) scroll = document.pageIndices.endInclusive.toFloat().div(2f).ceilToInt().times(2)
} }
}
}
} }

View File

@@ -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 blurtex0: Texture
private lateinit var blurtex1: Texture private lateinit var blurtex1: Texture
private lateinit var blurtex2: Texture private lateinit var blurtex2: Texture