From 6109e9b38c8ca10384319450a43c9a37b15507e0 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()