diff --git a/gfx/render/buffer.lua b/gfx/render/buffer.lua new file mode 100644 index 0000000..8e329b6 --- /dev/null +++ b/gfx/render/buffer.lua @@ -0,0 +1,232 @@ +local Util = require("util") + +local DEFAULT_C = " " +local DEFAULT_FG = colors.white +local DEFAULT_BG = colors.black + +local function ensureC(cVal) + return (cVal ~= nil and #cVal == 1 and cVal) or DEFAULT_C +end + +local WindowBuffer = {} + +-- Create a shallow wrapper for window object +function WindowBuffer.wrapWindow(w, defaultC, defaultFg, defaultBg, clearColor) + defaultC = ensureC(defaultC) + defaultFg = defaultFg or DEFAULT_FG + defaultBg = defaultBg or DEFAULT_BG + clearColor = clearColor or DEFAULT_BG + + local obj = {} + + function obj.blit(text, fg, bg) + w.blit(text, fg, bg) + end + + function obj.flush() end + function obj.getSize() + return w.getSize() + end + + function obj._setPixel(x, y, c, fg, bg) + w.setCursorPos(x, y) + w.setFgColor(fg or defaultFg) + w.setBgColor(bg or defaultBg) + w.write(c or defaultC) + end + + function obj.setPixel(x, y, c, fg, bg) + local width, height = obj.getSize() + Util.boundCheck(x, y, width, height) + obj._setPixel(x, y, c, fg, bg) + end + + -- Partial writes are not possible for native window access + obj._writePixel = obj._setPixel + obj.writePixel = obj.setPixel + + function obj.setCursorPos(x, y) + w.setCursorPos(x, y) + end + + function obj.getCursorPos() + return w.getCursorPos() + end + + function obj.clear() + w.setBgColor(clearColor) + --w.setFgColor(clearColor) -- Shouldn't matter for native clear + w.clear() + end + + return obj +end + +function WindowBuffer.new(width, height, clearC, clearFg, clearBg) + clearC = ensureC(clearC) + clearFg = clearFg or DEFAULT_FG + clearBg = clearBg or DEFAULT_BG + + local obj = {} + + local function setState(o, c, fg, bg) + o.c = c + o.bg = bg + o.fg = fg + return o + end + + local function writeState(o, c, fg, bg) + o.c = c or o.c + o.bg = bg or o.bg + o.fg = fg or o.fg + return o + end + + local buffer = {} + for i=1,height do + local line = {} + for j=1,width do + line[j] = setState({}) + end + buffer[i] = line + end + + local pX, pY = 1, 1 + + function obj.blit(text, fg, bg) + assert(#text == #fg, "Foreground color blit length must match text length") + assert(#text == #bg, "Background color blit length must match text length") + + local line = buffer[pY] + local baseX = pX + for i=1,math.min(#text, width - baseX - 1) do + local blitChar = line[i] + blitChar.c = text:sub(i, i) + blitChar.fg = fg:sub(i, i) + blitChar.bg = bg:sub(i, i) + end + + pX = pX + #text + end + + function obj._setPixel(x, y, c, fg, bg) + setState(buffer[y][x], c, fg, bg) + end + + function obj.setPixel(x, y, c, fg, bg) + Util.boundCheck(x, y, width, height) + obj._setPixel(x, y, c, fg, bg) + end + + function obj._writePixel(x, y, c, fg, bg) + writeState(buffer[y][x], c, fg, bg) + end + + function obj.writePixel(x, y, c, fg, bg) + Util.boundCheck(x, y, width, height) + obj._writePixel(x, y, c, fg, bg) + end + + function obj.setCursorPos(x, y) + pX, pY = x, y + end + + function obj.getCursorPos() + return pX, pY + end + + function obj.clear() + for i=1,height do + local line = buffer[i] + for j=1,width do + setState(line[j], clearC, clearFg, clearBg) + end + end + end + + -- TODO: Implement partial flush; obj.flush(win, offsetX, offsetY, width, height) + + function obj.flush(win, offsetX, offsetY) + for i=1,height do + local line = buffer[i] + for j=1,width do + local entry = line[j] + win._writePixel(j + offsetX, i + offsetY, entry.c, entry.fg, entry.bg) + end + end + end + + return obj +end + +function WindowBuffer.shallowChild(windowBuffer, width, height, offsetX, offsetY, clearC, clearFg, clearBg) + -- Assume static for now + local parentWidth, parentHeight = windowBuffer.getSize() + Util.boundCheck(offsetX, offsetY, parentWidth, parentHeight) + Util.boundCheck(offsetX + width, offsetY + height, parentWidth, parentHeight) + + clearC = ensureC(clearC) + clearFg = clearFg or DEFAULT_FG + clearBg = clearBg or DEFAULT_BG + + local obj = {} + + local pX, pY = 1, 1 + + local function setCursorPos() + windowBuffer.setCursorPos(pX + offsetX, pY + offsetY) + end + + function obj.blit(text, fg, bg) + setCursorPos() + windowBuffer.blit(text, fg, bg) + pX = pX + #text + end + + function obj._setPixel(c, fg, bg) + setCursorPos() + windowBuffer._setPixel(c, fg, bg) + end + + function obj.setPixel(c, fg, bg) + setCursorPos() + windowBuffer.setPixel(c, fg, bg) + end + + function obj._writePixel(c, fg, bg) + setCursorPos() + windowBuffer._writePixel(c, fg, bg) + end + + function obj.writePixel(c, fg, bg) + setCursorPos() + windowBuffer.writePixel(c, fg, bg) + end + + function obj.setCursorPos(x, y) + pX, pY = x, y + end + + function obj.getCursorPos() + return pX, pY + end + + function obj.getSize() + return width, height + end + + function obj.clear() + for i=1,height do + for j=1,width do + windowBuffer._setPixel(clearC, clearFg, clearBg) + end + end + end + + obj.flush = windowBuffer.flush() + + return obj +end + +return WindowBuffer \ No newline at end of file diff --git a/gfx/render/init.lua b/gfx/render/init.lua new file mode 100644 index 0000000..e69de29