From 474f3be4a722d05eed85f1f2386eaafd516750ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucio=20Andr=C3=A9s=20Illanes=20Albornoz?= Date: Wed, 11 Sep 2019 08:28:56 +0200 Subject: [PATCH] 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. --- assets/text/TODO | 24 ++++++++--------- libgui/GuiCanvasWxBackend.py | 41 ++++++++++++++++++++---------- libroar/RoarCanvasCommandsTools.py | 2 +- libroar/RoarCanvasWindow.py | 18 ++++++------- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/assets/text/TODO b/assets/text/TODO index e45546e..6845e94 100644 --- a/assets/text/TODO +++ b/assets/text/TODO @@ -1,20 +1,16 @@ 1) Implement ANSI CSI CU[BDPU] sequences & italic -2) Incremental auto{load,save} & {backup,restore} -3) Implement instrumentation & unit tests, document -4) Open and toggle a reference image in the background -5) Client-Server or Peer-to-Peer realtime collaboration -6) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.) -7) Hotkey & graphical interfaces to {composed,parametrised} tools -8) Layers, layout (e.g. for comics, zines, etc.) & asset management (e.g. kade, lion, etc.) & traits w/ {inserting,merging,linking} -9) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...) -10) Composition and parametrisation of tools from higher-order operators (brushes, filters, outlines, patterns & shaders) and unit tools; unit tools: +2) Implement instrumentation & unit tests, document +3) Open and toggle a reference image in the background +4) Client-Server or Peer-to-Peer realtime collaboration +5) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.) +6) Hotkey & graphical interfaces to {composed,parametrised} tools +7) Incremental auto{load,save} & {backup,restore} (needs Settings window) +8) GUI: a) switch from wxPython to GTK b) {copy,cut,insert from,paste}, {de,in}crease cell size c) MRU {directories,files} +9) Layers, layout (e.g. for comics, zines, etc.) & asset management (e.g. kade, lion, etc.) & traits w/ {inserting,merging,linking} +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) b) regions (crop, duplicate, erase, fill, invert, measure, pick, rotate, scale, select, shift, slice, tile, translate) 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 diff --git a/libgui/GuiCanvasWxBackend.py b/libgui/GuiCanvasWxBackend.py index 6051bd7..a26ca88 100644 --- a/libgui/GuiCanvasWxBackend.py +++ b/libgui/GuiCanvasWxBackend.py @@ -7,6 +7,27 @@ from GuiCanvasColours import Colours 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(): # {{{ _drawBrushPatch(self, eventDc, patch, point) def _drawBrushPatch(self, eventDc, patch, point): @@ -95,30 +116,22 @@ class GuiCanvasWxBackend(): else: return False # }}} - # {{{ getDeviceContext(self, parentWindow, viewRect) - def getDeviceContext(self, parentWindow, viewRect): + # {{{ getDeviceContext(self, clientSize, parentWindow, viewRect) + def getDeviceContext(self, clientSize, parentWindow, viewRect): if viewRect == (0, 0): eventDc = wx.BufferedDC(wx.ClientDC(parentWindow), self.canvasBitmap) else: - eventDc = wx.ClientDC(parentWindow) + eventDc = GuiBufferedDC(self, self.canvasBitmap, clientSize, wx.ClientDC(parentWindow), viewRect) self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None return eventDc # }}} - # {{{ onPaintEvent(self, canvasSize, cellSize, clientSize, panelWindow, viewRect) - def onPaintEvent(self, canvasSize, cellSize, clientSize, panelWindow, viewRect): + # {{{ onPaint(self, clientSize, panelWindow, viewRect) + def onPaint(self, clientSize, panelWindow, viewRect): if self.canvasBitmap != None: if viewRect == (0, 0): eventDc = wx.BufferedPaintDC(panelWindow, self.canvasBitmap) else: - canvasSize = [a - b for a, b in zip(canvasSize, 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) + eventDc = GuiBufferedDC(self, self.canvasBitmap, clientSize, wx.PaintDC(panelWindow), viewRect) # }}} # {{{ resize(self, canvasSize, cellSize): def resize(self, canvasSize, cellSize): diff --git a/libroar/RoarCanvasCommandsTools.py b/libroar/RoarCanvasCommandsTools.py index fd4e038..3f2308c 100644 --- a/libroar/RoarCanvasCommandsTools.py +++ b/libroar/RoarCanvasCommandsTools.py @@ -31,7 +31,7 @@ class RoarCanvasCommandsTools(): toolBar.ToggleTool(self.canvasTool.attrList[idx]["id"], True) self.update(toolName=self.currentTool.name) 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) setattr(canvasTool_, "attrDict", f.attrList[idx]) setattr(canvasTool_, "isSelect", True) diff --git a/libroar/RoarCanvasWindow.py b/libroar/RoarCanvasWindow.py index 78a75c5..9962201 100644 --- a/libroar/RoarCanvasWindow.py +++ b/libroar/RoarCanvasWindow.py @@ -38,12 +38,12 @@ class RoarCanvasWindow(GuiWindow): # }}} # {{{ 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: if patch == None: continue 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: self.canvas._commitPatch(patch); self.backend.drawPatch(eventDc, patch, self.GetViewStart()); # }}} @@ -64,7 +64,7 @@ class RoarCanvasWindow(GuiWindow): if self.canvas.resize(newSize, commitUndo): super().resize([a * b for a, b in zip(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: for numRow in range(oldSize[1]): for numNewCol in range(oldSize[0], newSize[0]): @@ -79,7 +79,7 @@ class RoarCanvasWindow(GuiWindow): def update(self, newSize, commitUndo=True, newCanvas=None): self.resize(newSize, commitUndo) 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 numCol in range(newSize[0]): self.backend.drawPatch(eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]], self.GetViewStart()) @@ -87,19 +87,19 @@ class RoarCanvasWindow(GuiWindow): # {{{ 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() if not self.applyTool(eventDc, False, keyChar, keyModifiers, None, None, None, None, self.commands.currentTool, viewRect): event.Skip() # }}} # {{{ 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()) # }}} # {{{ 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() mapPoint = self.backend.xlateEventPoint(event, eventDc, 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) 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) def onScroll(self, event): if self.canvas.dirtyCursor: 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.canvas.dirtyCursor = False event.Skip()