mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-17 19:25:20 -04:00
working on color screens
This commit is contained in:
parent
39b2c97010
commit
4ee76b146d
@ -1,91 +0,0 @@
|
|||||||
package li.cil.oc.client.gui
|
|
||||||
|
|
||||||
import li.cil.oc.Config
|
|
||||||
import net.minecraft.client.renderer.GLAllocation
|
|
||||||
import net.minecraft.client.renderer.Tessellator
|
|
||||||
import net.minecraft.client.renderer.texture.TextureManager
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import org.lwjgl.opengl.GL11
|
|
||||||
import scala.io.Source
|
|
||||||
|
|
||||||
object MonospaceFontRenderer {
|
|
||||||
private val font = new ResourceLocation(Config.resourceDomain, "textures/font/chars.png")
|
|
||||||
|
|
||||||
private val chars = Source.fromInputStream(MonospaceFontRenderer.getClass.getResourceAsStream("/assets/" + Config.resourceDomain + "/textures/font/chars.txt")).mkString
|
|
||||||
|
|
||||||
private var instance: Option[Renderer] = None
|
|
||||||
|
|
||||||
def init(textureManager: TextureManager) =
|
|
||||||
instance = instance.orElse(Some(new Renderer(textureManager)))
|
|
||||||
|
|
||||||
val (fontWidth, fontHeight) = (5, 9)
|
|
||||||
|
|
||||||
def drawString(value: Array[Char], x: Int, y: Int) = instance match {
|
|
||||||
case None => // Do nothing, not initialized.
|
|
||||||
case Some(renderer) => renderer.drawString(value, x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Renderer(private val textureManager: TextureManager) {
|
|
||||||
/** Display lists, one per char (renders quad with char's uv coords). */
|
|
||||||
private val charLists = GLAllocation.generateDisplayLists(256)
|
|
||||||
|
|
||||||
/** Buffer filled with char display lists to efficiently draw strings. */
|
|
||||||
private val listBuffer = GLAllocation.createDirectIntBuffer(512)
|
|
||||||
|
|
||||||
// Set up the display lists.
|
|
||||||
{
|
|
||||||
val (charWidth, charHeight) = (MonospaceFontRenderer.fontWidth * 2, MonospaceFontRenderer.fontHeight * 2)
|
|
||||||
val cols = 256 / charWidth
|
|
||||||
val uStep = charWidth / 256.0
|
|
||||||
val vStep = charHeight / 256.0
|
|
||||||
val t = Tessellator.instance
|
|
||||||
// Now create lists for all printable chars.
|
|
||||||
for (index <- 1 until 0xFF) {
|
|
||||||
val x = (index - 1) % cols
|
|
||||||
val y = (index - 1) / cols
|
|
||||||
val u = x * uStep
|
|
||||||
val v = y * vStep
|
|
||||||
GL11.glNewList(charLists + index, GL11.GL_COMPILE)
|
|
||||||
t.startDrawingQuads()
|
|
||||||
t.addVertexWithUV(0, charHeight, 0, u, v + vStep)
|
|
||||||
t.addVertexWithUV(charWidth, charHeight, 0, u + uStep, v + vStep)
|
|
||||||
t.addVertexWithUV(charWidth, 0, 0, u + uStep, v)
|
|
||||||
t.addVertexWithUV(0, 0, 0, u, v)
|
|
||||||
t.draw()
|
|
||||||
GL11.glTranslatef(charWidth, 0, 0)
|
|
||||||
GL11.glEndList()
|
|
||||||
}
|
|
||||||
// Special case for whitespace: just translate, don't render.
|
|
||||||
GL11.glNewList(charLists + ' ', GL11.GL_COMPILE)
|
|
||||||
GL11.glTranslatef(charWidth, 0, 0)
|
|
||||||
GL11.glEndList()
|
|
||||||
}
|
|
||||||
|
|
||||||
def drawString(value: Array[Char], x: Int, y: Int) = {
|
|
||||||
setTexture(textureManager, MonospaceFontRenderer.font)
|
|
||||||
GL11.glPushMatrix()
|
|
||||||
GL11.glTranslatef(x, y, 0)
|
|
||||||
GL11.glScalef(0.5f, 0.5f, 1)
|
|
||||||
GL11.glColor4f(1, 1, 1, 1)
|
|
||||||
for (c <- value) {
|
|
||||||
val index = 1 + chars.indexOf(c) match {
|
|
||||||
case -1 => chars.indexOf('?')
|
|
||||||
case i => i
|
|
||||||
}
|
|
||||||
listBuffer.put(charLists + index)
|
|
||||||
if (listBuffer.remaining == 0)
|
|
||||||
flush()
|
|
||||||
}
|
|
||||||
flush()
|
|
||||||
GL11.glPopMatrix()
|
|
||||||
}
|
|
||||||
|
|
||||||
private def flush() = {
|
|
||||||
listBuffer.flip()
|
|
||||||
GL11.glCallLists(listBuffer)
|
|
||||||
listBuffer.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def setTexture(tm: TextureManager, resource: ResourceLocation) = tm.bindTexture(resource)
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package li.cil.oc.client.gui
|
|||||||
|
|
||||||
import li.cil.oc.Config
|
import li.cil.oc.Config
|
||||||
import li.cil.oc.client.PacketSender
|
import li.cil.oc.client.PacketSender
|
||||||
|
import li.cil.oc.client.renderer.MonospaceFontRenderer
|
||||||
import li.cil.oc.common.tileentity
|
import li.cil.oc.common.tileentity
|
||||||
import net.minecraft.client.gui.{GuiScreen => MCGuiScreen}
|
import net.minecraft.client.gui.{GuiScreen => MCGuiScreen}
|
||||||
import net.minecraft.client.renderer.GLAllocation
|
import net.minecraft.client.renderer.GLAllocation
|
||||||
@ -11,6 +12,7 @@ import net.minecraft.util.ResourceLocation
|
|||||||
import org.lwjgl.input.Keyboard
|
import org.lwjgl.input.Keyboard
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
import li.cil.oc.util.PackedColor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This GUI shows the buffer of a single screen.
|
* This GUI shows the buffer of a single screen.
|
||||||
@ -47,11 +49,11 @@ class Screen(tileEntity: tileentity.Screen) extends MCGuiScreen {
|
|||||||
|
|
||||||
// Re-build display lists.
|
// Re-build display lists.
|
||||||
Screen.compileBackground(innerWidth, innerHeight)
|
Screen.compileBackground(innerWidth, innerHeight)
|
||||||
Screen.compileText(scale, screen.instance.lines)
|
Screen.compileText(scale, screen.instance.lines, screen.instance.colors, screen.instance.depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Must be called whenever the buffer of the underlying screen changes. */
|
/** Must be called whenever the buffer of the underlying screen changes. */
|
||||||
def updateText() = Screen.compileText(scale, screen.instance.lines)
|
def updateText() = Screen.compileText(scale, screen.instance.lines, screen.instance.colors, screen.instance.depth)
|
||||||
|
|
||||||
override def handleKeyboardInput() {
|
override def handleKeyboardInput() {
|
||||||
super.handleKeyboardInput()
|
super.handleKeyboardInput()
|
||||||
@ -182,14 +184,14 @@ object Screen {
|
|||||||
GL11.glEndList()
|
GL11.glEndList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private[gui] def compileText(scale: Double, lines: Array[Array[Char]]) =
|
private[gui] def compileText(scale: Double, lines: Array[Array[Char]], colors:Array[Array[Int]], depth: PackedColor.Depth.Value) =
|
||||||
if (textureManager.isDefined) {
|
if (textureManager.isDefined) {
|
||||||
GL11.glNewList(displayLists.get + 1, GL11.GL_COMPILE)
|
GL11.glNewList(displayLists.get + 1, GL11.GL_COMPILE)
|
||||||
|
|
||||||
GL11.glTranslatef(margin + innerMargin, margin + innerMargin, 0)
|
GL11.glTranslatef(margin + innerMargin, margin + innerMargin, 0)
|
||||||
GL11.glScaled(scale, scale, 1)
|
GL11.glScaled(scale, scale, 1)
|
||||||
lines.zipWithIndex.foreach {
|
lines.zip(colors).zipWithIndex.foreach {
|
||||||
case (line, i) => MonospaceFontRenderer.drawString(line, 0, i * MonospaceFontRenderer.fontHeight)
|
case ((line, color), i) => MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
GL11.glEndList()
|
GL11.glEndList()
|
||||||
|
137
li/cil/oc/client/renderer/MonospaceFontRenderer.scala
Normal file
137
li/cil/oc/client/renderer/MonospaceFontRenderer.scala
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package li.cil.oc.client.renderer
|
||||||
|
|
||||||
|
import li.cil.oc.util.{RenderState, PackedColor}
|
||||||
|
import li.cil.oc.{OpenComputers, Config}
|
||||||
|
import net.minecraft.client.renderer.GLAllocation
|
||||||
|
import net.minecraft.client.renderer.Tessellator
|
||||||
|
import net.minecraft.client.renderer.texture.TextureManager
|
||||||
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import org.lwjgl.opengl.GL11
|
||||||
|
import scala.io.Source
|
||||||
|
|
||||||
|
object MonospaceFontRenderer {
|
||||||
|
private val font = new ResourceLocation(Config.resourceDomain, "textures/font/chars.png")
|
||||||
|
|
||||||
|
private val chars = Source.fromInputStream(MonospaceFontRenderer.getClass.getResourceAsStream("/assets/" + Config.resourceDomain + "/textures/font/chars.txt")).mkString
|
||||||
|
|
||||||
|
private var instance: Option[Renderer] = None
|
||||||
|
|
||||||
|
def init(textureManager: TextureManager) = this.synchronized(
|
||||||
|
instance = instance.orElse(Some(new Renderer(textureManager))))
|
||||||
|
|
||||||
|
val (fontWidth, fontHeight) = (5, 9)
|
||||||
|
|
||||||
|
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Int], depth: PackedColor.Depth.Value) = instance match {
|
||||||
|
case None => OpenComputers.log.warning("Trying to render string with uninitialized MonospaceFontRenderer.")
|
||||||
|
case Some(renderer) => renderer.drawString(x, y, value, color, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Renderer(private val textureManager: TextureManager) {
|
||||||
|
/** Display lists, one per char (renders quad with char's uv coords). */
|
||||||
|
private val charLists = GLAllocation.generateDisplayLists(256)
|
||||||
|
|
||||||
|
/** Buffer filled with char display lists to efficiently draw strings. */
|
||||||
|
private val listBuffer = GLAllocation.createDirectIntBuffer(512)
|
||||||
|
|
||||||
|
private val (charWidth, charHeight) = (MonospaceFontRenderer.fontWidth * 2, MonospaceFontRenderer.fontHeight * 2)
|
||||||
|
private val cols = 256 / charWidth
|
||||||
|
private val uStep = charWidth / 256.0
|
||||||
|
private val vStep = charHeight / 256.0
|
||||||
|
|
||||||
|
// Set up the display lists.
|
||||||
|
{
|
||||||
|
val t = Tessellator.instance
|
||||||
|
// Now create lists for all printable chars.
|
||||||
|
for (index <- 1 until 0xFF) {
|
||||||
|
val x = (index - 1) % cols
|
||||||
|
val y = (index - 1) / cols
|
||||||
|
val u = x * uStep
|
||||||
|
val v = y * vStep
|
||||||
|
GL11.glNewList(charLists + index, GL11.GL_COMPILE)
|
||||||
|
t.startDrawingQuads()
|
||||||
|
t.addVertexWithUV(0, charHeight, 0, u, v + vStep)
|
||||||
|
t.addVertexWithUV(charWidth, charHeight, 0, u + uStep, v + vStep)
|
||||||
|
t.addVertexWithUV(charWidth, 0, 0, u + uStep, v)
|
||||||
|
t.addVertexWithUV(0, 0, 0, u, v)
|
||||||
|
t.draw()
|
||||||
|
GL11.glTranslatef(charWidth, 0, 0)
|
||||||
|
GL11.glEndList()
|
||||||
|
}
|
||||||
|
// Special case for whitespace: just translate, don't render.
|
||||||
|
GL11.glNewList(charLists + ' ', GL11.GL_COMPILE)
|
||||||
|
GL11.glTranslatef(charWidth, 0, 0)
|
||||||
|
GL11.glEndList()
|
||||||
|
}
|
||||||
|
|
||||||
|
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Int], depth: PackedColor.Depth.Value) = {
|
||||||
|
if (color.length != value.length) throw new IllegalArgumentException("Color count must match char count.")
|
||||||
|
|
||||||
|
textureManager.bindTexture(MonospaceFontRenderer.font)
|
||||||
|
GL11.glPushMatrix()
|
||||||
|
GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT)
|
||||||
|
GL11.glTranslatef(x, y, 0)
|
||||||
|
GL11.glScalef(0.5f, 0.5f, 1)
|
||||||
|
GL11.glDepthMask(false)
|
||||||
|
RenderState.makeItBlend()
|
||||||
|
|
||||||
|
// Background first. We try to merge adjacent backgrounds of the same
|
||||||
|
// color to reduce the number of quads we have to draw.
|
||||||
|
var cbg = 0x000000
|
||||||
|
var width = 0
|
||||||
|
for (col <- color.map(PackedColor.unpackBackground(_, depth))) {
|
||||||
|
if (col != cbg) {
|
||||||
|
draw(cbg, width)
|
||||||
|
cbg = col
|
||||||
|
width = 0
|
||||||
|
}
|
||||||
|
width = width + 1
|
||||||
|
}
|
||||||
|
draw(cbg, width)
|
||||||
|
|
||||||
|
// Foreground second. We only have to flush when the color changes, so
|
||||||
|
// unless every char has a different color this should be quite efficient.
|
||||||
|
var cfg = 0x000000
|
||||||
|
GL11.glColor3f(0, 0, 0)
|
||||||
|
for ((ch, col) <- value.zip(color.map(PackedColor.unpackForeground(_, depth)))) {
|
||||||
|
val index = 1 + chars.indexOf(ch) match {
|
||||||
|
case -1 => chars.indexOf('?')
|
||||||
|
case i => i
|
||||||
|
}
|
||||||
|
if (col != cfg) {
|
||||||
|
// Color changed, force flush and adjust colors.
|
||||||
|
flush()
|
||||||
|
cfg = col
|
||||||
|
GL11.glColor3ub(
|
||||||
|
(cfg & 0xFF0000 >> 16).toByte,
|
||||||
|
(cfg & 0x00FF00 >> 8).toByte,
|
||||||
|
(cfg & 0x0000FF).toByte)
|
||||||
|
}
|
||||||
|
listBuffer.put(charLists + index)
|
||||||
|
if (listBuffer.remaining == 0)
|
||||||
|
flush()
|
||||||
|
}
|
||||||
|
flush()
|
||||||
|
|
||||||
|
GL11.glPopAttrib()
|
||||||
|
GL11.glPopMatrix()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def draw(color: Int, width: Int) = if (color != 0 && width > 0) {
|
||||||
|
val t = Tessellator.instance
|
||||||
|
t.startDrawingQuads()
|
||||||
|
t.setColorOpaque_I(color)
|
||||||
|
t.addVertexWithUV(0, charHeight, 0, 0, vStep)
|
||||||
|
t.addVertexWithUV(charWidth, charHeight, 0, width * uStep, vStep)
|
||||||
|
t.addVertexWithUV(charWidth, 0, 0, width * uStep, 0)
|
||||||
|
t.addVertexWithUV(0, 0, 0, 0, 0)
|
||||||
|
t.draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def flush() = {
|
||||||
|
listBuffer.flip()
|
||||||
|
GL11.glCallLists(listBuffer)
|
||||||
|
listBuffer.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,7 +5,7 @@ import cpw.mods.fml.common.{TickType, ITickHandler}
|
|||||||
import java.util
|
import java.util
|
||||||
import java.util.concurrent.{TimeUnit, Callable}
|
import java.util.concurrent.{TimeUnit, Callable}
|
||||||
import li.cil.oc.Config
|
import li.cil.oc.Config
|
||||||
import li.cil.oc.client.gui.MonospaceFontRenderer
|
import li.cil.oc.client.renderer.MonospaceFontRenderer
|
||||||
import li.cil.oc.common.tileentity.Screen
|
import li.cil.oc.common.tileentity.Screen
|
||||||
import li.cil.oc.util.RenderState
|
import li.cil.oc.util.RenderState
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
@ -130,8 +130,8 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
|
|||||||
// Slightly offset the text so it doesn't clip into the screen.
|
// Slightly offset the text so it doesn't clip into the screen.
|
||||||
GL11.glTranslatef(0, 0, 0.01f)
|
GL11.glTranslatef(0, 0, 0.01f)
|
||||||
|
|
||||||
for ((line, i) <- screen.instance.lines.zipWithIndex) {
|
for (((line, color), i) <- screen.instance.lines.zip(screen.instance.colors).zipWithIndex) {
|
||||||
MonospaceFontRenderer.drawString(line, 0, i * MonospaceFontRenderer.fontHeight)
|
MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, screen.instance.depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
GL11.glEndList()
|
GL11.glEndList()
|
||||||
|
@ -3,12 +3,12 @@ package li.cil.oc.common.component
|
|||||||
import li.cil.oc.api.Persistable
|
import li.cil.oc.api.Persistable
|
||||||
import li.cil.oc.api.network.Visibility
|
import li.cil.oc.api.network.Visibility
|
||||||
import li.cil.oc.common.{tileentity, component}
|
import li.cil.oc.common.{tileentity, component}
|
||||||
import li.cil.oc.util.TextBuffer
|
import li.cil.oc.util.{PackedColor, TextBuffer}
|
||||||
import li.cil.oc.{Config, util, api}
|
import li.cil.oc.{Config, util, api}
|
||||||
import net.minecraft.nbt.NBTTagCompound
|
import net.minecraft.nbt.NBTTagCompound
|
||||||
|
|
||||||
class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) extends Persistable {
|
class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) extends Persistable {
|
||||||
private val buffer = new TextBuffer(maxResolution)
|
private val buffer = new TextBuffer(maxResolution, PackedColor.Depth.OneBit)
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
@ -16,6 +16,10 @@ class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) exten
|
|||||||
|
|
||||||
def lines = buffer.buffer
|
def lines = buffer.buffer
|
||||||
|
|
||||||
|
def colors = buffer.color
|
||||||
|
|
||||||
|
def depth = buffer.depth
|
||||||
|
|
||||||
def resolution = buffer.size
|
def resolution = buffer.size
|
||||||
|
|
||||||
def resolution_=(value: (Int, Int)) = {
|
def resolution_=(value: (Int, Int)) = {
|
||||||
|
100
li/cil/oc/util/PackedColor.scala
Normal file
100
li/cil/oc/util/PackedColor.scala
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package li.cil.oc.util
|
||||||
|
|
||||||
|
object PackedColor {
|
||||||
|
|
||||||
|
object Depth extends Enumeration {
|
||||||
|
val OneBit, EightBit, SixteenBit = Value
|
||||||
|
}
|
||||||
|
|
||||||
|
private val rMask32 = 0xFF0000
|
||||||
|
private val gMask32 = 0x00FF00
|
||||||
|
private val bMask32 = 0x0000FF
|
||||||
|
private val rShift32 = 16
|
||||||
|
private val gShift32 = 8
|
||||||
|
private val bShift32 = 0
|
||||||
|
|
||||||
|
// 7 6 5 4 3 2 1 0
|
||||||
|
// r r r g g g b b : 3.3.2
|
||||||
|
private val rMask8 = Integer.parseInt("11100000", 2)
|
||||||
|
private val gMask8 = Integer.parseInt("00011100", 2)
|
||||||
|
private val bMask8 = Integer.parseInt("00000011", 2)
|
||||||
|
private val rShift8 = 5
|
||||||
|
private val gShift8 = 2
|
||||||
|
private val bShift8 = 0
|
||||||
|
private val rScale8 = 255.0 / 0x7
|
||||||
|
private val gScale8 = 255.0 / 0x7
|
||||||
|
private val bScale8 = 255.0 / 0x3
|
||||||
|
|
||||||
|
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||||||
|
// r r r r r g g g g g g b b b b b : 5.6.5
|
||||||
|
private val rMask16 = Integer.parseInt("1111100000000000", 2)
|
||||||
|
private val gMask16 = Integer.parseInt("0000011111100000", 2)
|
||||||
|
private val bMask16 = Integer.parseInt("0000000000011111", 2)
|
||||||
|
private val rShift16 = 11
|
||||||
|
private val gShift16 = 5
|
||||||
|
private val bShift16 = 0
|
||||||
|
private val rScale16 = 255.0 / 0x1F
|
||||||
|
private val gScale16 = 255.0 / 0x3F
|
||||||
|
private val bScale16 = 255.0 / 0x1F
|
||||||
|
|
||||||
|
private def extractFrom1Bit(c: Short) = if (c == 0) 0x000000 else 0xFFFFFF
|
||||||
|
|
||||||
|
private def compressTo1Bit(c: Int) = (if (c == 0) 0 else 1).toShort
|
||||||
|
|
||||||
|
private def extractFrom8Bit(c: Short) = {
|
||||||
|
val r = ((((c & rMask8) >>> rShift8) * rScale8).toInt << rShift32) & rMask32
|
||||||
|
val g = ((((c & gMask8) >>> gShift8) * gScale8).toInt << gShift32) & gMask32
|
||||||
|
val b = ((((c & bMask8) >>> bShift8) * bScale8).toInt << bShift32) & bMask32
|
||||||
|
r | g | b
|
||||||
|
}
|
||||||
|
|
||||||
|
private def compressTo8Bit(c: Int) = {
|
||||||
|
val r = ((((c & rMask32) >>> rShift32) / rScale8).toInt << rShift8) & rMask8
|
||||||
|
val g = ((((c & gMask32) >>> gShift32) / gScale8).toInt << gShift8) & gMask8
|
||||||
|
val b = ((((c & bMask32) >>> bShift32) / bScale8).toInt << bShift8) & bMask8
|
||||||
|
(r | g | b).toShort
|
||||||
|
}
|
||||||
|
|
||||||
|
private def extractFrom16Bit(c: Short) = {
|
||||||
|
val r = ((((c & rMask16) >>> rShift16) * rScale16).toInt << rShift32) & rMask32
|
||||||
|
val g = ((((c & gMask16) >>> gShift16) * gScale16).toInt << gShift32) & gMask32
|
||||||
|
val b = ((((c & bMask16) >>> bShift16) * bScale16).toInt << bShift32) & bMask32
|
||||||
|
r | g | b
|
||||||
|
}
|
||||||
|
|
||||||
|
private def compressTo16Bit(c: Int) = {
|
||||||
|
val r = ((((c & rMask32) >>> rShift32) / rScale16).toInt << rShift16) & rMask16
|
||||||
|
val g = ((((c & gMask32) >>> gShift32) / gScale16).toInt << gShift16) & gMask16
|
||||||
|
val b = ((((c & bMask32) >>> bShift32) / bScale16).toInt << bShift16) & bMask16
|
||||||
|
(r | g | b).toShort
|
||||||
|
}
|
||||||
|
|
||||||
|
// Colors are packed: 0xFFFFBBBB (F = foreground, B = background)
|
||||||
|
private val fgShift = 16
|
||||||
|
private val bgShift = 0
|
||||||
|
|
||||||
|
def pack(foreground: Int, background: Int, depth: Depth.Value) =
|
||||||
|
depth match {
|
||||||
|
case Depth.OneBit => (compressTo1Bit(foreground) << fgShift) | (compressTo1Bit(background) << bgShift)
|
||||||
|
case Depth.EightBit => (compressTo8Bit(foreground) << fgShift) | (compressTo8Bit(background) << bgShift)
|
||||||
|
case Depth.SixteenBit => (compressTo16Bit(foreground) << fgShift) | (compressTo16Bit(background) << bgShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
def unpackForeground(color: Int, depth: Depth.Value) = {
|
||||||
|
val c = (color >>> fgShift).toShort
|
||||||
|
depth match {
|
||||||
|
case Depth.OneBit => extractFrom1Bit(c)
|
||||||
|
case Depth.EightBit => extractFrom8Bit(c)
|
||||||
|
case Depth.SixteenBit => extractFrom16Bit(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def unpackBackground(color: Int, depth: Depth.Value) = {
|
||||||
|
val c = (color >>> bgShift).toShort
|
||||||
|
depth match {
|
||||||
|
case Depth.OneBit => extractFrom1Bit(c)
|
||||||
|
case Depth.EightBit => extractFrom8Bit(c)
|
||||||
|
case Depth.SixteenBit => extractFrom16Bit(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package li.cil.oc.util
|
package li.cil.oc.util
|
||||||
|
|
||||||
import net.minecraft.nbt.NBTTagCompound
|
import net.minecraft.nbt.{NBTTagIntArray, NBTTagCompound, NBTTagList, NBTTagString}
|
||||||
import net.minecraft.nbt.NBTTagList
|
|
||||||
import net.minecraft.nbt.NBTTagString
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This stores chars in a 2D-Array and provides some manipulation functions.
|
* This stores chars in a 2D-Array and provides some manipulation functions.
|
||||||
@ -12,8 +10,30 @@ import net.minecraft.nbt.NBTTagString
|
|||||||
* relatively fast updates, given a smart algorithm (using copy()/fill()
|
* relatively fast updates, given a smart algorithm (using copy()/fill()
|
||||||
* instead of set()ing everything).
|
* instead of set()ing everything).
|
||||||
*/
|
*/
|
||||||
class TextBuffer(var width: Int, var height: Int) {
|
class TextBuffer(var width: Int, var height: Int, val depth: PackedColor.Depth.Value) {
|
||||||
def this(size: (Int, Int)) = this(size._1, size._2)
|
def this(size: (Int, Int), depth: PackedColor.Depth.Value) = this(size._1, size._2, depth)
|
||||||
|
|
||||||
|
private var foreground_ = 0xFFFFFF
|
||||||
|
|
||||||
|
private var background_ = 0x000000
|
||||||
|
|
||||||
|
private var packed = PackedColor.pack(foreground_, background_, depth)
|
||||||
|
|
||||||
|
def foreground = foreground_
|
||||||
|
|
||||||
|
def foreground_=(value: Int) = {
|
||||||
|
foreground_ = value
|
||||||
|
packed = PackedColor.pack(foreground_, background_, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
def background = background_
|
||||||
|
|
||||||
|
def background_=(value: Int) = {
|
||||||
|
background_ = value
|
||||||
|
packed = PackedColor.pack(foreground_, background_, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
var color = Array.fill(height, width)(packed)
|
||||||
|
|
||||||
var buffer = Array.fill(height, width)(' ')
|
var buffer = Array.fill(height, width)(' ')
|
||||||
|
|
||||||
@ -32,10 +52,13 @@ class TextBuffer(var width: Int, var height: Int) {
|
|||||||
val (w, h) = (iw max 1, ih max 1)
|
val (w, h) = (iw max 1, ih max 1)
|
||||||
if (width != w || height != h) {
|
if (width != w || height != h) {
|
||||||
val newBuffer = Array.fill(h, w)(' ')
|
val newBuffer = Array.fill(h, w)(' ')
|
||||||
(0 until (h min height)) foreach {
|
val newColor = Array.fill(h, w)(packed)
|
||||||
y => Array.copy(buffer(y), 0, newBuffer(y), 0, w min width)
|
(0 until (h min height)).foreach(y => {
|
||||||
}
|
Array.copy(buffer(y), 0, newBuffer(y), 0, w min width)
|
||||||
|
Array.copy(color(y), 0, newColor(y), 0, w min width)
|
||||||
|
})
|
||||||
buffer = newBuffer
|
buffer = newBuffer
|
||||||
|
color = newColor
|
||||||
width = w
|
width = w
|
||||||
height = h
|
height = h
|
||||||
true
|
true
|
||||||
@ -52,10 +75,12 @@ class TextBuffer(var width: Int, var height: Int) {
|
|||||||
else {
|
else {
|
||||||
var changed = false
|
var changed = false
|
||||||
val line = buffer(row)
|
val line = buffer(row)
|
||||||
|
val lineColor = color(row)
|
||||||
for (x <- col until ((col + s.length) min width)) if (x >= 0) {
|
for (x <- col until ((col + s.length) min width)) if (x >= 0) {
|
||||||
val c = s(x - col)
|
val c = s(x - col)
|
||||||
changed = changed || (line(x) != c)
|
changed = changed || (line(x) != c) || (lineColor(x) != packed)
|
||||||
line(x) = c
|
line(x) = c
|
||||||
|
lineColor(x) = packed
|
||||||
}
|
}
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
@ -68,9 +93,11 @@ class TextBuffer(var width: Int, var height: Int) {
|
|||||||
var changed = false
|
var changed = false
|
||||||
for (y <- (row max 0) until ((row + h) min height)) {
|
for (y <- (row max 0) until ((row + h) min height)) {
|
||||||
val line = buffer(y)
|
val line = buffer(y)
|
||||||
|
val lineColor = color(y)
|
||||||
for (x <- (col max 0) until ((col + w) min width)) {
|
for (x <- (col max 0) until ((col + w) min width)) {
|
||||||
changed = changed || (line(x) != c)
|
changed = changed || (line(x) != c) || (lineColor(x) != packed)
|
||||||
line(x) = c
|
line(x) = c
|
||||||
|
lineColor(x) = packed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
changed
|
changed
|
||||||
@ -97,13 +124,16 @@ class TextBuffer(var width: Int, var height: Int) {
|
|||||||
var changed = false
|
var changed = false
|
||||||
for (ny <- dy0 to dy1 by sy) {
|
for (ny <- dy0 to dy1 by sy) {
|
||||||
val nl = buffer(ny)
|
val nl = buffer(ny)
|
||||||
|
val nc = color(ny)
|
||||||
ny - ty match {
|
ny - ty match {
|
||||||
case oy if oy >= 0 && oy < height =>
|
case oy if oy >= 0 && oy < height =>
|
||||||
val ol = buffer(oy)
|
val ol = buffer(oy)
|
||||||
|
val oc = color(oy)
|
||||||
for (nx <- dx0 to dx1 by sx) nx - tx match {
|
for (nx <- dx0 to dx1 by sx) nx - tx match {
|
||||||
case ox if ox >= 0 && ox < width => {
|
case ox if ox >= 0 && ox < width => {
|
||||||
changed = changed || (nl(nx) != ol(ox))
|
changed = changed || (nl(nx) != ol(ox)) || (nc(nx) != oc(ox))
|
||||||
nl(nx) = ol(ox)
|
nl(nx) = ol(ox)
|
||||||
|
nc(nx) = oc(ox)
|
||||||
}
|
}
|
||||||
case _ => /* Got no source column. */
|
case _ => /* Got no source column. */
|
||||||
}
|
}
|
||||||
@ -118,19 +148,30 @@ class TextBuffer(var width: Int, var height: Int) {
|
|||||||
val h = nbt.getInteger("height")
|
val h = nbt.getInteger("height")
|
||||||
size = (w, h)
|
size = (w, h)
|
||||||
val b = nbt.getTagList("buffer")
|
val b = nbt.getTagList("buffer")
|
||||||
for (i <- 0 until (h min b.tagCount())) {
|
for (i <- 0 until (h min b.tagCount)) {
|
||||||
set(0, i, b.tagAt(i).asInstanceOf[NBTTagString].data)
|
val line = b.tagAt(i).asInstanceOf[NBTTagString].data
|
||||||
|
set(0, i, line)
|
||||||
|
}
|
||||||
|
val c = nbt.getTagList("color")
|
||||||
|
for (i <- 0 until (h min c.tagCount)) {
|
||||||
|
val line = c.tagAt(i).asInstanceOf[NBTTagIntArray].intArray
|
||||||
|
Array.copy(line, 0, color(i), 0, line.length min width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def writeToNBT(nbt: NBTTagCompound): Unit = {
|
def writeToNBT(nbt: NBTTagCompound): Unit = {
|
||||||
nbt.setInteger("width", width)
|
nbt.setInteger("width", width)
|
||||||
nbt.setInteger("height", height)
|
nbt.setInteger("height", height)
|
||||||
val b = new NBTTagList
|
val b = new NBTTagList()
|
||||||
for (i <- 0 until height) {
|
for (i <- 0 until height) {
|
||||||
b.appendTag(new NBTTagString("", String.valueOf(buffer(i))))
|
b.appendTag(new NBTTagString(null, String.valueOf(buffer(i))))
|
||||||
}
|
}
|
||||||
nbt.setTag("buffer", b)
|
nbt.setTag("buffer", b)
|
||||||
|
val c = new NBTTagList()
|
||||||
|
for (i <- 0 until height) {
|
||||||
|
c.appendTag(new NBTTagIntArray(null, color(i)))
|
||||||
|
}
|
||||||
|
nbt.setTag("color", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString = {
|
override def toString = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user