load "iupe-dbuffer.lua" dalosp.framebuffer = { get_settings = function (self) local extra = self.extra return { width = extra.width, height = extra.height, bpp = extra.bpp, zoom = extra.zoom, ppdir = extra.ppdir, flipx = extra.flipx, flipy = extra.flipy, } end, input_change = function (self, ind) local h = self:get_linked_input(ind) if ind == 2 then if h then local cfg, s = h:readfile() s, cfg = pcall(loadstring, "local cfg = " .. cfg .. "\nreturn cfg\n") if s then self:apply_config(cfg) self.extra.fb:prepare() self.extra.fb:draw() end end return end if h then h.getnextbyte = dalosp.framebuffer.getnextbyte end self:apply_config{} self.extra.fb:prepare() self.extra.fb:draw() end, activate = function (self) self.extra.dlg:show() end, configure = function (self) local extra = self.extra invert_lookup = { [1] = 0, [2] = 1, [4] = 2, [8] = 3, [24] = 4, [32] = 5, } lookup = { [0] = 1, [1] = 2, [2] = 4, [3] = 8, [4] = 24, [5] = 32, } local owidth, oheight, obpp, oppdir, oflipx, oflipy = extra.width, extra.height, extra.bpp, extra.ppdir, not not extra.flipx, not not extra.flipy local cb = function (dlg, pi) if pi < 0 then return end local width, height, bpp, dir width = iup.GetParamParam(dlg, 1).value + 0 height = iup.GetParamParam(dlg, 2).value + 0 bpp = iup.GetParamParam(dlg, 3).value + 0 dir = iup.GetParamParam(dlg, 4).value + 0 flipx = (iup.GetParamParam(dlg, 5).value + 0) ~= 0 flipy = (iup.GetParamParam(dlg, 6).value + 0) ~= 0 self:apply_config{ width = width, height = height, bpp = lookup[bpp], dir = dir, flipx = flipx, flipy = flipy } self.extra.fb:prepare() self.extra.fb:draw() end local s, nname = iup.GetParam(self.name .. " configuration", cb, [[ New name: %s Width: %i Height: %i Bpp: %l|1 bpp|2 bpp|4 bpp|8 bpp|24 bpp|32 bpp| Packed Pixels: %l|Little Endian|Big Endian| Flip X axis: %b Flip Y axis: %b ]], self.name, owidth, oheight, invert_lookup[obpp], oppdir, oflipx and 1 or 0, oflipy and 1 or 0) if not s then self:apply_config{ width = owidth, height = oheight, bpp = obpp, ppdir = oppdir, flipx = oflipx, flipy = oflipy } self.extra.fb:prepare() self.extra.fb:draw() else self.name = nname end end, apply_config = function (self, cfg) local extra = self.extra local fb = extra.fb local h = self:get_linked_input(1) if cfg.width then extra.width = cfg.width end if cfg.height then extra.height = cfg.height end if cfg.bpp then extra.bpp = cfg.bpp end if cfg.ppdir then extra.ppdir = cfg.ppdir end if cfg.flipx then extra.flipx = cfg.flipx end if cfg.flipy then extra.flipy = cfg.flipy end if cfg.zoom then extra.zoom = cfg.zoom end local zoom = 2 ^ extra.zoom local posx = fb.posx local posy = fb.posy local dx = fb.dx + 0 local dy = fb.dy + 0 local fw = math.floor(extra.width * zoom) local fh = math.floor(extra.height * zoom) if dx >= (fb.xmax - fb.xmin) then posx = math.floor((fw - dx) / 2) end if dy >= (fb.ymax - fb.ymin) then posy = math.floor((fh - dy) / 2) end fb.xmax = fw fb.ymax = fh cfg = { h = h, width = extra.width, height = extra.height, bpp = extra.bpp, ppdir = extra.ppdir, flipx = extra.flipx, flipy = extra.flipy, zoom = zoom, x = -posx, y = -posy, fw = fw, fh = fh, } fb.cfg = cfg local cvwidth, cvheight = fb.width or 0, fb.height or 0 cvwidth, cvheight = cvwidth + 0, cvheight + 0 end, getnextbyte = function (h) return h:tell() ~= h:getsize() and h:readU8() or 0 end, getbit = function (cfg) local rc local h = cfg.h local stateful = cfg.stateful if cfg.bpp >= 8 then return end stateful = cfg.stateful if not stateful or bit.band(stateful, 0x7f) == 0 then stateful = h:getnextbyte() * 2 + 1 else stateful = stateful * 2 end cfg.stateful = stateful return bit.band(bit.rshift(stateful, 8), 1) end, getnextpixel = function (cfg) local rc local r, g, b local h = cfg.h local getbit = function() return dalosp.framebuffer.getbit(cfg) end if cfg.bpp == 1 then rc = getbit() * 255 r, g, b = rc, rc, rc elseif cfg.bpp == 2 then rc = getbit() * 2 rc = (rc + getbit()) * 255 / 3 r, g, b = rc, rc, rc elseif cfg.bpp == 4 then rc = getbit() * 8 rc = rc + getbit() * 4 rc = rc + getbit() * 2 rc = (rc + getbit()) * 255 / 15 r, g, b = rc, rc, rc elseif cfg.bpp == 8 then rc = h:getnextbyte() r, g, b = rc, rc, rc elseif cfg.bpp == 24 then r = h:getnextbyte() g = h:getnextbyte() b = h:getnextbyte() elseif cfg.bpp == 32 then r = h:getnextbyte() g = h:getnextbyte() b = h:getnextbyte() h:getnextbyte() end return r, g, b end, prepare = function (self) local cfg = self.cfg local h = cfg.h local width = cfg.width local height = cfg.height local bpp = cfg.bpp local ppdir = cfg.ppdir local flipx = cfg.flipx local flipy = not cfg.flipy local cim = im.ImageCreate(width, height, im.RGB, im.BYTE) self.cim = cim cim:Clear() if not h then return iup.DEFAULT end local getnextpixel = self.getnextpixel h:seek(0) cfg.stateful = 0 local r, g, b = cim[0], cim[1], cim[2] for y = 0, height - 1 do for x = 0, width - 1 do local px, py = flipx and width - x - 1 or x, flipy and height - y - 1 or y r[py][px], g[py][px], b[py][px] = getnextpixel(cfg) end end return iup.DEFAULT end, draw = function (self) local cvdb = self.cvdb if not cvdb then return iup.DEFAULT end cvdb:Clear() local cim = self.cim local cfg = self.cfg cim:cdCanvasPutImageRect(cvdb, cfg.x, cvdb:InvertYAxis(cfg.y + cfg.fh), cfg.fw, cfg.fh, 0, 0, 0, 0) cvdb:Flush() end, resize_cb = function (self, width, height) self.dx = width self.dy = height self.posx = self.posx self.posy = self.posy iupep.dbuffer.resize_cb(self, width, height) self.obj:apply_config{} self:draw() end, fimg2canvas = function (self, ix, iy) local cfg = self.cfg local cx, cy return ix + cfg.x, iy + cfg.y end, canvas2fimg = function (self, cx, cy) local cfg = self.cfg return cx - cfg.x, cy - cfg.y end, img2canvas = function (self, ix, iy) local cfg = self.cfg return math.floor(ix * cfg.zoom + cfg.x), math.floor(iy * cfg.zoom + cfg.y) end, canvas2img = function (self, cx, cy) local cfg = self.cfg return math.floor((cx - cfg.x) / cfg.zoom), math.floor((cy - cfg.y) / cfg.zoom) end, scroll_cb = function (self, op, posx, posy) self:resize_cb(self.width, self.height) end, wheel_cb = function (self, delta, x, y, status) local ox, oy = self:canvas2img(x, y) local opx, opy = self.cfg.x, self.cfg.y self.obj:apply_config { zoom = self.obj.extra.zoom + (delta / 10) } local nx, ny = self:img2canvas(ox, oy) local dx, dy = ox - nx, oy - ny self.posx, self.posy = -opx - dx, -opy - dy self:resize_cb(self.width, self.height) end, motion_cb = function (self, x, y) local ox, oy = self:canvas2img(x, y) local nx, ny = self:img2canvas(ox, oy) end, create = function (d, tab, settings) tab.ninputs = 2 tab.noutputs = 0 tab.otype = dalos.objtype.LUA_VIEWER tab.activate = dalosp.framebuffer.activate tab.configure = dalosp.framebuffer.configure tab.input_change = dalosp.framebuffer.input_change tab.default_name = "Framebuffer" tab.ntype = "Framebuffer" tab.get_settings = dalosp.framebuffer.get_settings local extra = { width = settings.width or 256, height = settings.height or 256, bpp = settings.bpp or 32, ppdir = settings.dir or 0, flipx = settings.flipx, flipy = settings.flipy, zoom = settings.zoom or 0, } local obj = dalos.object(d, tab, extra) obj.apply_config = dalosp.framebuffer.apply_config local fb = iup.canvas { expand = "Yes", font = "Courier, 8", action = iupep.dbuffer.action, map_cb = iupep.dbuffer.map_cb, unmap_cb = iupep.dbuffer.unmap_cb, resize_cb = dalosp.framebuffer.resize_cb, motion_cb = dalosp.framebuffer.motion_cb, draw = dalosp.framebuffer.draw, scroll_cb = dalosp.framebuffer.scroll_cb, wheel_cb = dalosp.framebuffer.wheel_cb, prepare = dalosp.framebuffer.prepare, img2canvas = dalosp.framebuffer.img2canvas, canvas2img = dalosp.framebuffer.canvas2img, fimg2canvas = dalosp.framebuffer.img2canvas, canvas2fimg = dalosp.framebuffer.canvas2img, getnextpixel = dalosp.framebuffer.getnextpixel, rastersize = extra.width .. "x" .. extra.height, scrollbar = "Yes", dx = 1, dy = 1, linex = 1, liney = 1, xmax = 0, ymax = 0, xautohide = "No", yautohide = "No", obj = obj, } local dlg = iup.dialog { iup.vbox { fb, iup.hbox { iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = -4 } fb:resize_cb(fb.width, fb.height) end, title = "1:16", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = -3 } fb:resize_cb(fb.width, fb.height) end, title = "1:8", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = -2 } fb:resize_cb(fb.width, fb.height) end, title = "1:4", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = -1 } fb:resize_cb(fb.width, fb.height) end, title = "1:2", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = 0 } fb:resize_cb(fb.width, fb.height) end, title = "1:1", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = 1 } fb:resize_cb(fb.width, fb.height) end, title = "2:1", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = 2 } fb:resize_cb(fb.width, fb.height) end, title = "4:1", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = 3 } fb:resize_cb(fb.width, fb.height) end, title = "8:1", }, iup.fill{}, iup.button { action = function () fb.posx, fb.posy = 0, 0 obj:apply_config { zoom = 4 } fb:resize_cb(fb.width, fb.height) end, title = "16:1", }, iup.fill{}, }, }, size = "320x200", title = obj.name, shrink = "Yes", } extra.dlg = dlg extra.fb = fb obj:apply_config{} fb:prepare() fb:draw() return obj end, } dalos.framebuffer = dalosp.framebuffer.create dalos:register_obj("Framebuffer", dalos.framebuffer, "Basic Viewers")