diff --git a/assets/disk0/home/jpdectest.js b/assets/disk0/home/jpdectest.js index 3925dbe..c0f6206 100644 --- a/assets/disk0/home/jpdectest.js +++ b/assets/disk0/home/jpdectest.js @@ -1,3 +1,7 @@ +if (!exec_args[1]) { + printerrln("Usage: jpdectest image.jpg") +} + filesystem.open("A", exec_args[1], "R") let status = com.getStatusCode(0) @@ -20,7 +24,8 @@ println(`dim: ${imgw}x${imgh}`) println(`converting to displayable format...`) // convert colour -graphics.imageToDisplayableFormat(imageData, -1048577, imgw, imgh, 4, 2) +graphics.setGraphicsMode(0) +graphics.imageToDisplayableFormat(imageData, -1048577, imgw, imgh, 4, 1) sys.free(imageData) sys.free(infile) diff --git a/assets/disk0/home/jpdectesthigh.js b/assets/disk0/home/jpdectesthigh.js new file mode 100644 index 0000000..fcff3a9 --- /dev/null +++ b/assets/disk0/home/jpdectesthigh.js @@ -0,0 +1,31 @@ +if (!exec_args[1]) { + printerrln("Usage: jpdectesthigh image.jpg") +} + +filesystem.open("A", exec_args[1], "R") + +let status = com.getStatusCode(0) +let infile = undefined +if (0 != status) return status + + +let fileLen = filesystem.getFileLen("A") +println(`DMA reading ${fileLen} bytes from disk...`) +infile = sys.malloc(fileLen) +dma.comToRam(0, 0, infile, fileLen) + + +println("decoding") + +// decode +const [imgw, imgh, imageData] = graphics.decodeImageResample(infile, fileLen, -1, -1) + +println(`dim: ${imgw}x${imgh}`) +println(`converting to displayable format...`) + +// convert colour +graphics.setGraphicsMode(4) +graphics.imageToDirectCol(imageData, -1048577, -1310721, imgw, imgh, 4, 0) + +sys.free(imageData) +sys.free(infile) diff --git a/terranmon.txt b/terranmon.txt index b174d9d..9268a24 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -366,6 +366,11 @@ Type: 0: 256-Colour frame 1: 256-Colour frame with palette data 2: 4096-Colour frame (stored as two byte-planes) + 16: Series of JPEGs + 18: Series of PNGs + 20: Series of TGAs + 21: Series of TGA/GZs + 255: Every frame specifies the type TYPE 0 FRAME - uint32 SIZE OF FRAMEDATA @@ -380,4 +385,13 @@ TYPE 2 FRAME - uint32 SIZE OF FRAMEDATA BYTE-PLANE 1 * FRAMEDATA COMPRESSED IN GZIP uint32 SIZE OF FRAMEDATA BYTE-PLANE 2 - * FRAMEDATA COMPRESSED IN GZIP \ No newline at end of file + * FRAMEDATA COMPRESSED IN GZIP + +TYPE 16+ FRAME - + uint32 SIZE OF FRAMEDATA BYTE-PLANE 1 + * FRAMEDATA (COMPRESSED IN GZIP for TGA/GZ) + +TYPE 255 FRAME - + uint16 TYPE OF FRAMEDATA + uint32 SIZE OF FRAMEDATA + * FRAMEDATA \ No newline at end of file diff --git a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt index b573385..4bf2455 100644 --- a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt @@ -361,12 +361,32 @@ class GraphicsJSR223Delegate(val vm: VM) { 3f/16f,5f/16f,1f/16f, ) - private val bayerKernel = intArrayOf( - 0,8,2,10, - 12,4,14,6, - 3,11,1,9, - 15,7,13,5, - ).map { (it.toFloat() + 0.5f) / 16f }.toFloatArray() + private val bayerKernels = arrayOf( + intArrayOf( + 0,8,2,10, + 12,4,14,6, + 3,11,1,9, + 15,7,13,5, + ), + intArrayOf( + 8,2,10,0, + 4,14,6,12, + 11,1,9,3, + 7,13,5,15, + ), + intArrayOf( + 7,13,5,15, + 8,2,10,0, + 4,14,6,12, + 11,1,9,3, + ), + intArrayOf( + 15,7,13,5, + 0,8,2,10, + 12,4,14,6, + 3,11,1,9, + ) + ).map{ it.map { (it.toFloat() + 0.5f) / 16f }.toFloatArray() } /** * This method always assume that you're using the default palette @@ -382,7 +402,7 @@ class GraphicsJSR223Delegate(val vm: VM) { for (k in 0L until len) { val x = (k % width).toInt() val y = (k / width).toInt() - val t = bayerKernel[4 * (y % 4) + (x % 4)] + val t = bayerKernels[0][4 * (y % 4) + (x % 4)] val r = vm.peek(srcPtr + channels * k + 0)!!.toUint().toFloat() / 255f val g = vm.peek(srcPtr + channels * k + 1)!!.toUint().toFloat() / 255f @@ -473,4 +493,37 @@ class GraphicsJSR223Delegate(val vm: VM) { } } + fun imageToDirectCol(srcPtr: Int, destRG: Int, destBA: Int, width: Int, height: Int, channels: Int, pattern: Int = 0) { + val useAlpha = (channels == 4) + val sign = if (destRG >= 0) 1 else -1 + val len = width * height + if (destRG * destBA < 0) throw IllegalArgumentException("Both destination memories must be on the same domain (both being Usermem or HWmem)") + + for (k in 0L until len) { + val x = (k % width).toInt() + val y = (k / width).toInt() + val t = bayerKernels[pattern][4 * (y % 4) + (x % 4)] + + val r = vm.peek(srcPtr + channels * k + 0)!!.toUint().toFloat() / 255f + val g = vm.peek(srcPtr + channels * k + 1)!!.toUint().toFloat() / 255f + val b = vm.peek(srcPtr + channels * k + 2)!!.toUint().toFloat() / 255f + val a = if (useAlpha) vm.peek(srcPtr + channels * k + 3)!!.toUint().toFloat() / 255f else 1f + + // default palette is 16-16-16 level RGB (plus 15 shades of grey) + + val r1 = t / 15f + r + val g1 = t / 15f + g + val b1 = t / 15f + b + val a1 = t / 15f + a + + val ra = floor(15f * r1) + val ga = floor(15f * g1) + val ba = floor(15f * b1) + val aa = floor(15f * a1) + + vm.poke(destRG + k*sign, (ra.shl(4) or ga).toByte()) + vm.poke(destBA + k*sign, (ba.shl(4) or aa).toByte()) + } + } + } \ No newline at end of file diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index 9eeb164..4e66790 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -40,6 +40,7 @@ data class SuperGraphicsAddonConfig( ) class ReferenceGraphicsAdapter(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT) +class ReferenceGraphicsAdapter2(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT, SuperGraphicsAddonConfig(2)) class ReferenceLikeLCD(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_PMLCD) /** @@ -197,7 +198,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi val adi = addr.toInt() if (framebuffer2 != null) { return when (addr - 262144) { - in 0 until 250880 -> framebuffer2[addr] + in 0 until 250880 -> framebuffer2[addr - 262144] else -> null } } @@ -222,7 +223,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi when (addr - 262144) { in 0 until 250880 -> { lastUsedColour = byte - framebuffer2[addr] = byte + framebuffer2[addr - 262144] = byte return } } @@ -758,8 +759,32 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi chrrom.pixels.position(0) framebufferOut.setColor(-1);framebufferOut.fill() -// if (isRefSize && graphicsMode == 4 && ) - if (isRefSize && (graphicsMode == 1 || graphicsMode == 2)) { + if (graphicsMode == 4 && framebuffer2 != null) { + for (y in 0 until HEIGHT) { + var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8) + if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt() + val xs = (0 + xoff).coerceIn(0, WIDTH - 1)..(WIDTH - 1 + xoff).coerceIn(0, WIDTH - 1) + + if (xoff in -(WIDTH - 1) until WIDTH) { + for (x in xs) { + val rg = framebuffer[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559 + val ba = framebuffer2[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559 + val r = rg.ushr(4).and(15) + val g = rg.and(15) + val b = ba.ushr(4).and(15) + val a = ba.and(15) + framebufferOut.setColor( + r.shl(28) or r.shl(24) or + g.shl(20) or g.shl(16) or + b.shl(12) or b.shl(8) or + a.shl(4) or a + ) + framebufferOut.drawPixel(x, y) + } + } + } + } + else if (isRefSize && (graphicsMode == 1 || graphicsMode == 2)) { val layerOrder = (if (graphicsMode == 1) LAYERORDERS4 else LAYERORDERS2)[layerArrangement] for (y in 0..223) { var xoff = scanlineOffsets[2L * y].toUint().shl(8) or scanlineOffsets[2L * y + 1].toUint() diff --git a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java index 96f40cb..4163ed0 100644 --- a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java +++ b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java @@ -37,7 +37,7 @@ public class AppLoader { String diskPath = "assets/disk0"; - EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", diskPath, 560, 448); + EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter2", diskPath, 560, 448); EmulInstance reference2 = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceLikeLCD", diskPath, 560, 448); EmulInstance term = new EmulInstance(vm, "net.torvald.tsvm.peripheral.Term", diskPath, 720, 480); EmulInstance portable = new EmulInstance(vm, "net.torvald.tsvm.peripheral.CharacterLCDdisplay", diskPath, 628, 302); diff --git a/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt b/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt index 01b0d82..a1d1399 100644 --- a/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt +++ b/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt @@ -73,7 +73,7 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe if (loaderInfo.display != null) { val loadedClass = Class.forName(loaderInfo.display) val loadedClassConstructor = loadedClass.getConstructor(String::class.java, vm::class.java) - val loadedClassInstance = loadedClassConstructor.newInstance("./assets", vm) + val loadedClassInstance = loadedClassConstructor.newInstance("./assets", vm, ) gpu = (loadedClassInstance as GraphicsAdapter) vm.getIO().blockTransferPorts[0].attachDevice(TestDiskDrive(vm, 0, File(loaderInfo.diskPath)))