mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-10 18:44:05 +09:00
loading and rendering btexbin
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
package net.torvald.terrarum.btex
|
package net.torvald.terrarum.btex
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx
|
import com.badlogic.gdx.Gdx
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.files.FileHandle
|
||||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
import com.badlogic.gdx.graphics.*
|
||||||
import com.badlogic.gdx.graphics.Pixmap
|
|
||||||
import com.badlogic.gdx.graphics.PixmapIO
|
|
||||||
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.graphics.glutils.FrameBuffer
|
||||||
@@ -16,9 +14,11 @@ import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustf
|
|||||||
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.ui.Toolkit
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
|
import net.torvald.terrarum.utils.JsonFetcher
|
||||||
import net.torvald.terrarumsansbitmap.MovableType
|
import net.torvald.terrarumsansbitmap.MovableType
|
||||||
import net.torvald.terrarumsansbitmap.gdx.CodepointSequence
|
import net.torvald.terrarumsansbitmap.gdx.CodepointSequence
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.RandomAccessFile
|
||||||
import java.util.zip.Deflater
|
import java.util.zip.Deflater
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +36,7 @@ class BTeXDocument : Disposable {
|
|||||||
var theEdition = ""
|
var theEdition = ""
|
||||||
|
|
||||||
var textWidth = 480
|
var textWidth = 480
|
||||||
var lineHeightInPx = 24
|
val lineHeightInPx = 24
|
||||||
var pageLines = 24
|
var pageLines = 24
|
||||||
val textHeight: Int
|
val textHeight: Int
|
||||||
get() = pageLines * lineHeightInPx
|
get() = pageLines * lineHeightInPx
|
||||||
@@ -58,6 +58,60 @@ class BTeXDocument : Disposable {
|
|||||||
val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff)
|
val DEFAULT_ORNAMENTS_COL = Color(0x3f3c3b_ff)
|
||||||
|
|
||||||
private fun String.escape() = this.replace("\"", "\\\"")
|
private fun String.escape() = this.replace("\"", "\\\"")
|
||||||
|
|
||||||
|
fun fromFile(fileHandle: FileHandle) = fromFile(fileHandle.file())
|
||||||
|
|
||||||
|
fun fromFile(file: File): BTeXDocument {
|
||||||
|
val doc = BTeXDocument()
|
||||||
|
|
||||||
|
val ra = RandomAccessFile(file, "r")
|
||||||
|
val DOM = ClusteredFormatDOM(ra)
|
||||||
|
|
||||||
|
// get meta file
|
||||||
|
val meta = Clustfile(DOM, "/bibliography.json")
|
||||||
|
if (!meta.exists()) throw IllegalStateException("No bibliography.json found on the archive")
|
||||||
|
val metaReader = meta.readBytes().toString(Common.CHARSET).reader()
|
||||||
|
val metaJson = JsonFetcher.readFromJsonString(metaReader)
|
||||||
|
|
||||||
|
doc.theTitle = metaJson["title"].asString()
|
||||||
|
doc.theSubtitle = metaJson["subtitle"].asString()
|
||||||
|
doc.theAuthor = metaJson["author"].asString()
|
||||||
|
doc.theEdition = metaJson["edition"].asString()
|
||||||
|
val pageCount = metaJson["pages"].asInt()
|
||||||
|
doc.context = metaJson["context"].asString()
|
||||||
|
doc.font = metaJson["font"].asString()
|
||||||
|
doc.inner = metaJson["inner"].asString()
|
||||||
|
doc.papersize = metaJson["papersize"].asString()
|
||||||
|
doc.fromArchive = true
|
||||||
|
doc.pageTextures = ArrayList()
|
||||||
|
|
||||||
|
|
||||||
|
println("Title: ${doc.theTitle}")
|
||||||
|
println("Pages: $pageCount")
|
||||||
|
|
||||||
|
for (page in 0 until pageCount) {
|
||||||
|
Clustfile(DOM, "/${page}.png").also {
|
||||||
|
if (!it.exists()) throw IllegalStateException("No file '${page}.png' on the archive")
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if (page == 0) {
|
||||||
|
doc.textWidth = texture.regionWidth - 2 * doc.pageMarginH
|
||||||
|
doc.pageLines = (texture.regionHeight - 2 * doc.pageMarginH) / doc.lineHeightInPx
|
||||||
|
|
||||||
|
println("Page dimension: (${texture.regionWidth}x${texture.regionHeight}) (${doc.pageDimensionWidth}x${doc.pageDimensionHeight})")
|
||||||
|
}
|
||||||
|
tempFile.delete() // deleting also affects file descriptor juggling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ra.close()
|
||||||
|
|
||||||
|
return doc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val pages = ArrayList<BTeXPage>()
|
internal val pages = ArrayList<BTeXPage>()
|
||||||
@@ -72,7 +126,7 @@ class BTeXDocument : Disposable {
|
|||||||
get() = pages[currentPage]
|
get() = pages[currentPage]
|
||||||
|
|
||||||
val pageIndices: IntRange
|
val pageIndices: IntRange
|
||||||
get() = pages.indices
|
get() = if (fromArchive) pageTextures.indices else pages.indices
|
||||||
|
|
||||||
internal val linesPrintedOnPage = ArrayList<Int>()
|
internal val linesPrintedOnPage = ArrayList<Int>()
|
||||||
|
|
||||||
@@ -92,6 +146,7 @@ class BTeXDocument : Disposable {
|
|||||||
* Must be called on a thread with GL context!
|
* Must be called on a thread with GL context!
|
||||||
*/
|
*/
|
||||||
fun finalise() {
|
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")
|
if (isFinalised) throw IllegalStateException("Page is already been finalised")
|
||||||
|
|
||||||
pageTextures = ArrayList()
|
pageTextures = ArrayList()
|
||||||
@@ -100,7 +155,7 @@ class BTeXDocument : Disposable {
|
|||||||
val camera = OrthographicCamera(pageDimensionWidth.toFloat(), pageDimensionHeight.toFloat())
|
val camera = OrthographicCamera(pageDimensionWidth.toFloat(), pageDimensionHeight.toFloat())
|
||||||
val batch = FlippingSpriteBatch()
|
val batch = FlippingSpriteBatch()
|
||||||
|
|
||||||
pages.forEach { page ->
|
pages.forEachIndexed { pageNum, page ->
|
||||||
val fbo = FrameBuffer(Pixmap.Format.RGBA8888, pageDimensionWidth, pageDimensionHeight, false)
|
val fbo = FrameBuffer(Pixmap.Format.RGBA8888, pageDimensionWidth, pageDimensionHeight, false)
|
||||||
fbo.inAction(null, null) {
|
fbo.inAction(null, null) {
|
||||||
|
|
||||||
@@ -113,6 +168,7 @@ class BTeXDocument : Disposable {
|
|||||||
blendNormalStraightAlpha(batch)
|
blendNormalStraightAlpha(batch)
|
||||||
batch.inUse {
|
batch.inUse {
|
||||||
page.render(0f, batch, 0, 0, pageMarginH, pageMarginV)
|
page.render(0f, batch, 0, 0, pageMarginH, pageMarginV)
|
||||||
|
printPageNumber(batch, pageNum, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +185,9 @@ class BTeXDocument : Disposable {
|
|||||||
pageTextures.forEach { it.texture.dispose() }
|
pageTextures.forEach { it.texture.dispose() }
|
||||||
pageFrameBuffers.forEach { it.dispose() }
|
pageFrameBuffers.forEach { it.dispose() }
|
||||||
}
|
}
|
||||||
|
else if (fromArchive) {
|
||||||
|
pageTextures.forEach { it.texture.dispose() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun serialise(archiveFile: File) {
|
fun serialise(archiveFile: File) {
|
||||||
@@ -136,7 +195,6 @@ class BTeXDocument : Disposable {
|
|||||||
|
|
||||||
val diskFile = ClusteredFormatDOM.createNewArchive(archiveFile, Common.CHARSET, "", 0x7FFFF)
|
val diskFile = ClusteredFormatDOM.createNewArchive(archiveFile, Common.CHARSET, "", 0x7FFFF)
|
||||||
val DOM = ClusteredFormatDOM(diskFile)
|
val DOM = ClusteredFormatDOM(diskFile)
|
||||||
val tempFile = Gdx.files.external("./.btex-export.png")
|
|
||||||
|
|
||||||
val json = """
|
val json = """
|
||||||
{
|
{
|
||||||
@@ -164,18 +222,20 @@ class BTeXDocument : Disposable {
|
|||||||
|
|
||||||
fbo.inAction(null, null) {
|
fbo.inAction(null, null) {
|
||||||
val pixmap = Pixmap.createFromFrameBuffer(0, 0, fbo.width, fbo.height)
|
val pixmap = Pixmap.createFromFrameBuffer(0, 0, fbo.width, fbo.height)
|
||||||
|
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)
|
||||||
outstream.write(tempFile.readBytes())
|
outstream.write(tempFile.readBytes())
|
||||||
outstream.flush(); outstream.close()
|
outstream.flush(); outstream.close()
|
||||||
|
tempFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DOM.changeDiskCapacity(diskFile.length().div(4096f).ceilToInt())
|
DOM.changeDiskCapacity(diskFile.length().div(4096f).ceilToInt())
|
||||||
tempFile.delete()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isFinalised = false; private set
|
var isFinalised = false; private set
|
||||||
|
var fromArchive = false; private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends draw call to the list. The draw call must be prepared manually so that they would not overflow.
|
* Appends draw call to the list. The draw call must be prepared manually so that they would not overflow.
|
||||||
@@ -199,13 +259,16 @@ 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)
|
if (isFinalised || fromArchive)
|
||||||
pages[page].render(frameDelta, batch, x, y, pageMarginH, pageMarginV)
|
|
||||||
else
|
|
||||||
batch.draw(pageTextures[page], x.toFloat(), y.toFloat())
|
batch.draw(pageTextures[page], x.toFloat(), y.toFloat())
|
||||||
|
else {
|
||||||
|
pages[page].render(frameDelta, batch, x, y, pageMarginH, pageMarginV)
|
||||||
|
printPageNumber(batch, page, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// paint page number
|
private fun printPageNumber(batch: SpriteBatch, page: Int, x: Int, y: Int) {
|
||||||
val num = "${page+1}"
|
val num = "${page + 1}"
|
||||||
val numW = TinyAlphNum.getWidth(num)
|
val numW = TinyAlphNum.getWidth(num)
|
||||||
val numX = if (context == "tome") {
|
val numX = if (context == "tome") {
|
||||||
if (page % 2 == 1)
|
if (page % 2 == 1)
|
||||||
@@ -216,7 +279,7 @@ class BTeXDocument : Disposable {
|
|||||||
else {
|
else {
|
||||||
x + (pageDimensionWidth - numW) / 2
|
x + (pageDimensionWidth - numW) / 2
|
||||||
}
|
}
|
||||||
val numY = y + pageDimensionHeight - 2*pageMarginV - 4
|
val numY = y + pageDimensionHeight - 2 * pageMarginV - 4
|
||||||
|
|
||||||
if (page == 0 && context != "tome" || page in tocPageStart until endOfPageStart) {
|
if (page == 0 && context != "tome" || page in tocPageStart until endOfPageStart) {
|
||||||
batch.color = DEFAULT_ORNAMENTS_COL
|
batch.color = DEFAULT_ORNAMENTS_COL
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import kotlin.system.measureTimeMillis
|
|||||||
*/
|
*/
|
||||||
class BTeXTest : ApplicationAdapter() {
|
class BTeXTest : ApplicationAdapter() {
|
||||||
|
|
||||||
val filePath = "btex.xml"
|
// val filePath = "btex.btexbin"
|
||||||
// val filePath = "literature/en/daniel_defoe_robinson_crusoe.xml"
|
val filePath = "literature/en/daniel_defoe_robinson_crusoe.btexbin"
|
||||||
// val filePath = "literature/ruRU/anton_chekhov_palata_no_6.xml"
|
// val filePath = "literature/ruRU/anton_chekhov_palata_no_6.xml"
|
||||||
// val filePath = "literature/koKR/yisang_nalgae.xml"
|
// val filePath = "literature/koKR/yisang_nalgae.xml"
|
||||||
|
|
||||||
@@ -47,25 +47,36 @@ class BTeXTest : ApplicationAdapter() {
|
|||||||
|
|
||||||
bg = TextureRegion(Texture(Gdx.files.internal("test_assets/real_bg_with_guides.png")))
|
bg = TextureRegion(Texture(Gdx.files.internal("test_assets/real_bg_with_guides.png")))
|
||||||
|
|
||||||
measureTimeMillis {
|
val isBookFinalised = filePath.endsWith(".btexbin")
|
||||||
val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"))
|
|
||||||
document = f.first
|
|
||||||
documentHandler = f.second
|
|
||||||
}.also {
|
|
||||||
println("Time spent on typesetting [ms]: $it")
|
|
||||||
}
|
|
||||||
|
|
||||||
measureTimeMillis {
|
if (!isBookFinalised) {
|
||||||
document.finalise()
|
measureTimeMillis {
|
||||||
documentHandler.dispose()
|
val f = BTeXParser.invoke(Gdx.files.internal("./assets/mods/basegame/books/$filePath"))
|
||||||
}.also {
|
document = f.first
|
||||||
println("Time spent on finalising [ms]: $it")
|
documentHandler = f.second
|
||||||
}
|
}.also {
|
||||||
|
println("Time spent on typesetting [ms]: $it")
|
||||||
|
}
|
||||||
|
|
||||||
measureTimeMillis {
|
measureTimeMillis {
|
||||||
document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btexbin")}"))
|
document.finalise()
|
||||||
}.also {
|
documentHandler.dispose()
|
||||||
println("Time spent on serialisation [ms]: $it")
|
}.also {
|
||||||
|
println("Time spent on finalising [ms]: $it")
|
||||||
|
}
|
||||||
|
|
||||||
|
measureTimeMillis {
|
||||||
|
document.serialise(File("./assets/mods/basegame/books/${filePath.replace(".xml", ".btexbin")}"))
|
||||||
|
}.also {
|
||||||
|
println("Time spent on serialisation [ms]: $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
measureTimeMillis {
|
||||||
|
document = BTeXDocument.fromFile(Gdx.files.internal("./assets/mods/basegame/books/$filePath"))
|
||||||
|
}.also {
|
||||||
|
println("Time spent on loading [ms]: $it")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user