From 72b3c7bac962bb6573585d10a3e1c65b0ebbcd75 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 7 Feb 2019 10:09:40 -0500 Subject: [PATCH] canvas improvements --- sys/apis/ui/canvas.lua | 252 +++++++++++++++++++++-------------------- 1 file changed, 128 insertions(+), 124 deletions(-) diff --git a/sys/apis/ui/canvas.lua b/sys/apis/ui/canvas.lua index 3fbc15d..01328dc 100644 --- a/sys/apis/ui/canvas.lua +++ b/sys/apis/ui/canvas.lua @@ -220,45 +220,6 @@ function Canvas:clear(bg, fg) end end -function Canvas:punch(rect) - local offset = { x = 0, y = 0 } - - - self.regions:subRect(rect.x + offset.x, rect.y + offset.y, rect.ex + offset.x, rect.ey + offset.y) -end - -function Canvas:blitClipped(device, offset) - offset = { x = self.x, y = self.y } - local parent = self.parent - while parent do - offset.x = offset.x + parent.x - 1 - offset.y = offset.y + parent.y - 1 - parent = parent.parent - end - for _,region in ipairs(self.regions.region) do - self:blitRect(device, - { x = region[1], - y = region[2], - ex = region[3], - ey = region[4] }, - { x = region[1] + offset.x - 1, y = region[2] + offset.y - 1 }) - end -end - -function Canvas:redraw(device) --- self:dirty() --- self:render(device) - if #self.layers > 0 then - for _,layer in pairs(self.layers) do - self:punch(layer) - end - self:blitClipped(device) - else - self:renderLayers(device) - end - self:clean() -end - function Canvas:isDirty() for i = 1, #self.lines do if self.lines[i].dirty then @@ -284,92 +245,7 @@ function Canvas:clean() end end -function Canvas:renderLayers(device, offset) - if not offset then - offset = { x = self.x, y = self.y } - end - if #self.layers > 0 then - self.regions = Region.new(1, 1, self.ex, self.ey) - - for i = 1, #self.layers do - local canvas = self.layers[i] - if canvas.visible then - - -- punch out this area from the parent's canvas - self:punch(canvas) - - -- get the area to render for this layer - canvas.regions = Region.new(canvas.x, canvas.y, canvas.ex, canvas.ey) - - -- determine if we should render this layer by punching - -- out any layers that overlap this one - for j = i + 1, #self.layers do - if self.layers[j].visible then - canvas:punch(self.layers[j]) - end - end - end - end - - for _, canvas in ipairs(self.layers) do - if canvas.visible and #canvas.regions.region > 0 then - canvas:renderLayers(device, { - x = canvas.x, --offset.x + self.x - 1, - y = canvas.y, - }) - end - end - - self:blitClipped(device, offset) - - --elseif #self.regions.region > 0 then - -- self:blitClipped(device, offset) - - else - offset = { x = self.x, y = self.y } - local parent = self.parent - while parent do - offset.x = offset.x + parent.x - 1 - offset.y = offset.y + parent.y - 1 - parent = parent.parent - end - self:blitRect(device, nil, offset) - end - self:clean() -end - -function Canvas:render(device) - --_G._p = self - if #self.layers > 0 then - self:renderLayers(device) - else - self:blitRect(device) - self:clean() - end -end - -function Canvas:blitRect(device, src, tgt) - src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 } - tgt = tgt or self - - for i = 0, src.ey - src.y do - local line = self.lines[src.y + i + (self.offy or 0)] - if line and line.dirty then - local t, fg, bg = line.text, line.fg, line.bg - if src.x > 1 or src.ex < self.ex then - t = _sub(t, src.x, src.ex) - fg = _sub(fg, src.x, src.ex) - bg = _sub(bg, src.x, src.ex) - end - device.setCursorPos(tgt.x, tgt.y + i) - device.blit(t, fg, bg) - end - end - --os.sleep(.1) -end - function Canvas:applyPalette(palette) - local lookup = { } for n = 1, 16 do lookup[self.palette[2 ^ (n - 1)]] = palette[2 ^ (n - 1)] @@ -384,4 +260,132 @@ function Canvas:applyPalette(palette) self.palette = palette end +function Canvas:render(device) + if #self.layers > 0 then + self:__renderLayers(device, { x = 0, y = 0 }) + else + self:__blitRect(device) + self:clean() + end +end + +-- regions are comprised of absolute values that coorespond to the output device. +-- canvases have coordinates relative to their parent. +-- canvas layer's stacking order is determined by the position within the array. +-- layers in the beginning of the array are overlayed by layers further down in +-- the array. +function Canvas:__renderLayers(device, offset) + if #self.layers > 0 then + self.regions = self.regions or Region.new(self.x, self.y, self.ex, self.ey) + + for i = 1, #self.layers do + local canvas = self.layers[i] + if canvas.visible then + + -- punch out this area from the parent's canvas + self:__punch(canvas, offset) + + -- get the area to render for this layer + canvas.regions = Region.new( + canvas.x + offset.x, + canvas.y + offset.y, + canvas.ex + offset.x, + canvas.ey + offset.y) + + -- punch out any layers that overlap this one + for j = i + 1, #self.layers do + if self.layers[j].visible then + canvas:__punch(self.layers[j], offset) + end + end + if #canvas.regions.region > 0 then + canvas:__renderLayers(device, { + x = canvas.x + offset.x - 1, + y = canvas.y + offset.y - 1, + }) + end + canvas.regions = nil + end + end + + self:__blitClipped(device, offset) + self.regions = nil + + elseif self.regions and #self.regions.region > 0 then + self:__blitClipped(device, offset) + self.regions = nil + + else + offset = { x = self.x, y = self.y } + local parent = self.parent + while parent do + offset.x = offset.x + parent.x - 1 + offset.y = offset.y + parent.y - 1 + parent = parent.parent + end + self:__blitRect(device, nil, offset) + self.regions = nil + end + self:clean() +end + +function Canvas:__blitClipped(device, offset) + for _,region in ipairs(self.regions.region) do + self:__blitRect(device, + { x = region[1] - offset.x, + y = region[2] - offset.y, + ex = region[3] - offset.x, + ey = region[4] - offset.y}, + { x = region[1], y = region[2] }) + end +end + +function Canvas:__punch(rect, offset) + self.regions:subRect( + rect.x + offset.x, + rect.y + offset.y, + rect.ex + offset.x, + rect.ey + offset.y) +end + +function Canvas:__blitRect(device, src, tgt) + src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 } + tgt = tgt or self + + --[[ + -- for visualizing updates on the screen + local drew + for i = 0, src.ey - src.y do + local line = self.lines[src.y + i + (self.offy or 0)] + if line and line.dirty then + drew = true + local t, fg, bg = line.text, line.fg, line.bg + if src.x > 1 or src.ex < self.ex then + t = _sub(t, src.x, src.ex) + fg = _rep(1, src.ex-src.x + 1) + bg = _rep(2, src.ex-src.x + 1) + end + device.setCursorPos(tgt.x, tgt.y + i) + device.blit(t, fg, bg) + end + end + if drew then + os.sleep(.3) + end + ]] + for i = 0, src.ey - src.y do + local line = self.lines[src.y + i + (self.offy or 0)] + if line and line.dirty then + local t, fg, bg = line.text, line.fg, line.bg + if src.x > 1 or src.ex < self.ex then + t = _sub(t, src.x, src.ex) + fg = _sub(fg, src.x, src.ex) + bg = _sub(bg, src.x, src.ex) + end + device.setCursorPos(tgt.x, tgt.y + i) + device.blit(t, fg, bg) + end + end +end + return Canvas