libgui/GuiCanvasWxBackend.py:GuiBufferedDC(): implement double-buffered wx.MemoryDC() honouring view{Rect,Size}.

libgui/GuiCanvasWxBackend.py:{getDeviceContext,onPaint}(): use GuiBufferedDC() if viewRect > (0, 0).
libroar/RoarCanvas{CommandsTools,Window}.py: updated.
assets/text/TODO: updated.
This commit is contained in:
Lucio Andrés Illanes Albornoz 2019-09-11 08:28:56 +02:00
parent 968e7d45c5
commit 6109e9b38c
4 changed files with 47 additions and 38 deletions

View File

@ -1,20 +1,16 @@
1) Implement ANSI CSI CU[BDPU] sequences & italic 1) Implement ANSI CSI CU[BDPU] sequences & italic
2) Incremental auto{load,save} & {backup,restore} 2) Implement instrumentation & unit tests, document
3) Implement instrumentation & unit tests, document 3) Open and toggle a reference image in the background
4) Open and toggle a reference image in the background 4) Client-Server or Peer-to-Peer realtime collaboration
5) Client-Server or Peer-to-Peer realtime collaboration 5) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.)
6) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.) 6) Hotkey & graphical interfaces to {composed,parametrised} tools
7) Hotkey & graphical interfaces to {composed,parametrised} tools 7) Incremental auto{load,save} & {backup,restore} (needs Settings window)
8) Layers, layout (e.g. for comics, zines, etc.) & asset management (e.g. kade, lion, etc.) & traits w/ {inserting,merging,linking} 8) GUI: a) switch from wxPython to GTK b) {copy,cut,insert from,paste}, {de,in}crease cell size c) MRU {directories,files}
9) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...) 9) Layers, layout (e.g. for comics, zines, etc.) & asset management (e.g. kade, lion, etc.) & traits w/ {inserting,merging,linking}
10) Composition and parametrisation of tools from higher-order operators (brushes, filters, outlines, patterns & shaders) and unit tools; unit tools: 10) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...)
11) Composition and parametrisation of tools from higher-order operators (brushes, filters, outlines, patterns & shaders) and unit tools; unit tools:
a) geometric primitives (arrow, circle, cloud/speech bubble, curve, heart, hexagon, line, pentagon, polygon, rhombus, triangle, square, star) a) geometric primitives (arrow, circle, cloud/speech bubble, curve, heart, hexagon, line, pentagon, polygon, rhombus, triangle, square, star)
b) regions (crop, duplicate, erase, fill, invert, measure, pick, rotate, scale, select, shift, slice, tile, translate) b) regions (crop, duplicate, erase, fill, invert, measure, pick, rotate, scale, select, shift, slice, tile, translate)
c) text (edit, Unicode sets) c) text (edit, Unicode sets)
11) GUI:
a) Settings panel
b) switch from wxPython to GTK
c) {fix,reduce} flickering when viewRect > (0, 0)
d) {copy,cut,insert from,paste}, {de,in}crease cell size
vim:ff=dos tw=0 vim:ff=dos tw=0

View File

@ -7,6 +7,27 @@
from GuiCanvasColours import Colours from GuiCanvasColours import Colours
import math, wx import math, wx
class GuiBufferedDC(wx.MemoryDC):
# {{{ __del__(self)
def __del__(self):
self.dc.Blit(0, 0, *self.viewSize, self, 0, 0)
self.SelectObject(wx.NullBitmap)
# }}}
# {{{ __init__(self, backend, buffer, clientSize, dc, viewRect)
def __init__(self, backend, buffer, clientSize, dc, viewRect):
super().__init__()
canvasSize = [a - b for a, b in zip(backend.canvasSize, viewRect)]
clientSize = [math.ceil(m / n) for m, n in zip(clientSize, backend.cellSize)]
viewRect = [m * n for m, n in zip(backend.cellSize, viewRect)]
viewSize = [min(m, n) for m, n in zip(canvasSize, clientSize)]
viewSize = [m * n for m, n in zip(backend.cellSize, viewSize)]
bitmap = wx.Bitmap(viewSize); self.SelectObject(bitmap);
bufferDc = wx.MemoryDC(); bufferDc.SelectObject(buffer);
self.Blit(0, 0, *viewSize, bufferDc, *viewRect)
bufferDc.SelectObject(wx.NullBitmap)
self.dc, self.viewSize = dc, viewSize
# }}}
class GuiCanvasWxBackend(): class GuiCanvasWxBackend():
# {{{ _drawBrushPatch(self, eventDc, patch, point) # {{{ _drawBrushPatch(self, eventDc, patch, point)
def _drawBrushPatch(self, eventDc, patch, point): def _drawBrushPatch(self, eventDc, patch, point):
@ -95,30 +116,22 @@ class GuiCanvasWxBackend():
else: else:
return False return False
# }}} # }}}
# {{{ getDeviceContext(self, parentWindow, viewRect) # {{{ getDeviceContext(self, clientSize, parentWindow, viewRect)
def getDeviceContext(self, parentWindow, viewRect): def getDeviceContext(self, clientSize, parentWindow, viewRect):
if viewRect == (0, 0): if viewRect == (0, 0):
eventDc = wx.BufferedDC(wx.ClientDC(parentWindow), self.canvasBitmap) eventDc = wx.BufferedDC(wx.ClientDC(parentWindow), self.canvasBitmap)
else: else:
eventDc = wx.ClientDC(parentWindow) eventDc = GuiBufferedDC(self, self.canvasBitmap, clientSize, wx.ClientDC(parentWindow), viewRect)
self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None
return eventDc return eventDc
# }}} # }}}
# {{{ onPaintEvent(self, canvasSize, cellSize, clientSize, panelWindow, viewRect) # {{{ onPaint(self, clientSize, panelWindow, viewRect)
def onPaintEvent(self, canvasSize, cellSize, clientSize, panelWindow, viewRect): def onPaint(self, clientSize, panelWindow, viewRect):
if self.canvasBitmap != None: if self.canvasBitmap != None:
if viewRect == (0, 0): if viewRect == (0, 0):
eventDc = wx.BufferedPaintDC(panelWindow, self.canvasBitmap) eventDc = wx.BufferedPaintDC(panelWindow, self.canvasBitmap)
else: else:
canvasSize = [a - b for a, b in zip(canvasSize, viewRect)] eventDc = GuiBufferedDC(self, self.canvasBitmap, clientSize, wx.PaintDC(panelWindow), viewRect)
clientSize = [math.ceil(m / n) for m, n in zip(clientSize, cellSize)]
viewSize = [min(m, n) for m, n in zip(canvasSize, clientSize)]
viewSize = [m * n for m, n in zip(cellSize, viewSize)]
canvasDc = wx.MemoryDC(); canvasDc.SelectObject(self.canvasBitmap);
viewDc = wx.MemoryDC(); viewBitmap = wx.Bitmap(viewSize); viewDc.SelectObject(viewBitmap);
viewDc.Blit(0, 0, *viewSize, canvasDc, *[m * n for m, n in zip(cellSize, viewRect)])
canvasDc.SelectObject(wx.NullBitmap); viewDc.SelectObject(wx.NullBitmap);
eventDc = wx.BufferedPaintDC(panelWindow, viewBitmap)
# }}} # }}}
# {{{ resize(self, canvasSize, cellSize): # {{{ resize(self, canvasSize, cellSize):
def resize(self, canvasSize, cellSize): def resize(self, canvasSize, cellSize):

View File

@ -31,7 +31,7 @@ class RoarCanvasCommandsTools():
toolBar.ToggleTool(self.canvasTool.attrList[idx]["id"], True) toolBar.ToggleTool(self.canvasTool.attrList[idx]["id"], True)
self.update(toolName=self.currentTool.name) self.update(toolName=self.currentTool.name)
viewRect = self.parentCanvas.GetViewStart() viewRect = self.parentCanvas.GetViewStart()
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas, viewRect) eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas, viewRect)
self.parentCanvas.applyTool(eventDc, True, None, None, self.parentCanvas.brushPos, False, False, False, self.currentTool, viewRect) self.parentCanvas.applyTool(eventDc, True, None, None, self.parentCanvas.brushPos, False, False, False, self.currentTool, viewRect)
setattr(canvasTool_, "attrDict", f.attrList[idx]) setattr(canvasTool_, "attrDict", f.attrList[idx])
setattr(canvasTool_, "isSelect", True) setattr(canvasTool_, "isSelect", True)

View File

@ -38,12 +38,12 @@ class RoarCanvasWindow(GuiWindow):
# }}} # }}}
# {{{ dispatchDeltaPatches(self, deltaPatches) # {{{ dispatchDeltaPatches(self, deltaPatches)
def dispatchDeltaPatches(self, deltaPatches): def dispatchDeltaPatches(self, deltaPatches):
eventDc = self.backend.getDeviceContext(self, self.GetViewStart()) eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
for patch in deltaPatches: for patch in deltaPatches:
if patch == None: if patch == None:
continue continue
elif patch[0] == "resize": elif patch[0] == "resize":
del eventDc; self.resize(patch[1:], False); eventDc = self.backend.getDeviceContext(self, self.GetViewStart()); del eventDc; self.resize(patch[1:], False); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart());
else: else:
self.canvas._commitPatch(patch); self.backend.drawPatch(eventDc, patch, self.GetViewStart()); self.canvas._commitPatch(patch); self.backend.drawPatch(eventDc, patch, self.GetViewStart());
# }}} # }}}
@ -64,7 +64,7 @@ class RoarCanvasWindow(GuiWindow):
if self.canvas.resize(newSize, commitUndo): if self.canvas.resize(newSize, commitUndo):
super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)]) super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)])
self.backend.resize(newSize, self.backend.cellSize) self.backend.resize(newSize, self.backend.cellSize)
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self, viewRect); viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
if deltaSize[0] > 0: if deltaSize[0] > 0:
for numRow in range(oldSize[1]): for numRow in range(oldSize[1]):
for numNewCol in range(oldSize[0], newSize[0]): for numNewCol in range(oldSize[0], newSize[0]):
@ -79,7 +79,7 @@ class RoarCanvasWindow(GuiWindow):
def update(self, newSize, commitUndo=True, newCanvas=None): def update(self, newSize, commitUndo=True, newCanvas=None):
self.resize(newSize, commitUndo) self.resize(newSize, commitUndo)
self.canvas.update(newSize, newCanvas) self.canvas.update(newSize, newCanvas)
eventDc = self.backend.getDeviceContext(self, self.GetViewStart()) eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
for numRow in range(newSize[1]): for numRow in range(newSize[1]):
for numCol in range(newSize[0]): for numCol in range(newSize[0]):
self.backend.drawPatch(eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]], self.GetViewStart()) self.backend.drawPatch(eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]], self.GetViewStart())
@ -87,19 +87,19 @@ class RoarCanvasWindow(GuiWindow):
# {{{ onKeyboardInput(self, event) # {{{ onKeyboardInput(self, event)
def onKeyboardInput(self, event): def onKeyboardInput(self, event):
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self, viewRect); viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
keyChar, keyModifiers = chr(event.GetUnicodeKey()), event.GetModifiers() keyChar, keyModifiers = chr(event.GetUnicodeKey()), event.GetModifiers()
if not self.applyTool(eventDc, False, keyChar, keyModifiers, None, None, None, None, self.commands.currentTool, viewRect): if not self.applyTool(eventDc, False, keyChar, keyModifiers, None, None, None, None, self.commands.currentTool, viewRect):
event.Skip() event.Skip()
# }}} # }}}
# {{{ onLeaveWindow(self, event) # {{{ onLeaveWindow(self, event)
def onLeaveWindow(self, event): def onLeaveWindow(self, event):
eventDc = self.backend.getDeviceContext(self, self.GetViewStart()) eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, self.GetViewStart()) self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, self.GetViewStart())
# }}} # }}}
# {{{ onMouseInput(self, event) # {{{ onMouseInput(self, event)
def onMouseInput(self, event): def onMouseInput(self, event):
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self, viewRect); viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
mouseDragging, mouseLeftDown, mouseRightDown = event.Dragging(), event.LeftIsDown(), event.RightIsDown() mouseDragging, mouseLeftDown, mouseRightDown = event.Dragging(), event.LeftIsDown(), event.RightIsDown()
mapPoint = self.backend.xlateEventPoint(event, eventDc, viewRect) mapPoint = self.backend.xlateEventPoint(event, eventDc, viewRect)
if not self.applyTool(eventDc, True, None, None, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, self.commands.currentTool, viewRect): if not self.applyTool(eventDc, True, None, None, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, self.commands.currentTool, viewRect):
@ -107,13 +107,13 @@ class RoarCanvasWindow(GuiWindow):
# }}} # }}}
# {{{ onPaint(self, event) # {{{ onPaint(self, event)
def onPaint(self, event): def onPaint(self, event):
self.backend.onPaintEvent(self.canvas.size, self.cellSize, self.GetClientSize(), self, self.GetViewStart()) self.backend.onPaint(self.GetClientSize(), self, self.GetViewStart())
# }}} # }}}
# {{{ onScroll(self, event) # {{{ onScroll(self, event)
def onScroll(self, event): def onScroll(self, event):
if self.canvas.dirtyCursor: if self.canvas.dirtyCursor:
viewRect = self.GetViewStart() viewRect = self.GetViewStart()
eventDc = self.backend.getDeviceContext(self, viewRect) eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect)
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect) self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect)
self.canvas.dirtyCursor = False self.canvas.dirtyCursor = False
event.Skip() event.Skip()