From 966ca7a95bee0e8a2611a8dfa717c53660361179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucio=20Andr=C3=A9s=20Illanes=20Albornoz?= Date: Tue, 24 Sep 2019 14:03:16 +0200 Subject: [PATCH] Various bugfixes & usability improvements. 1) Directly render characters to canvas DC w/ clipping. 2) Fix background colour toolbar icon bitmaps. 3) Line tool: correctly set brush colours whenever updated. 4) Line tool: fix artifacts bug. 5) Render tool cursor transparently at opacity 200 (out of 255.) assets/text/TODO: updated. --- assets/text/TODO | 23 +++--- libgui/GuiCanvasWxBackend.py | 146 ++++++++++++++++++++-------------- libroar/RoarCanvasCommands.py | 4 +- libroar/RoarCanvasWindow.py | 2 +- libtools/ToolLine.py | 33 ++++---- 5 files changed, 120 insertions(+), 88 deletions(-) diff --git a/assets/text/TODO b/assets/text/TODO index f873bbc..9be9329 100644 --- a/assets/text/TODO +++ b/assets/text/TODO @@ -16,17 +16,16 @@ d) replace resize buttons w/ {-,edit box,+} buttons & lock button re: ratio (ty lol3) Release roadmap: -1) BUG: a) select line tool b) set origin point c) move mouse about d) incr brush size e) set target point d) undo -2) BUG: a) new canvas/startup b) place rect c) move mouse d) undo status changes -3) BUG: a) start w/ spoke-vxplion.txt b) scroll down c) fix w/ canvas resize -4) BUG: fix & finish Arabic/RTL text tool support -5) BUG: a) text tool b) paste text c) undo -6) BUG: a) tile once b) tile twice -7) {copy,cut,delete,insert from,paste}, edit asset in new canvas, import from {canvas,object} -8) add hotkeys.txt mIRC art canvas to help menu -9) {load,store} LRU {open,save} directory -10) operators: crop, scale, shift, slice -11) auto{load,save} & {backup,restore} -12) tools: unicode block elements +1) BUG: a) start w/ spoke-vxplion.txt b) scroll down c) fix w/ canvas resize +2) BUG: fix & finish Arabic/RTL text tool support +3) BUG: a) text tool b) paste text c) undo +4) BUG: a) tile once b) tile twice +5) {copy,cut,delete,insert from,paste}, edit asset in new canvas, import from {canvas,object} +6) add hotkeys.txt mIRC art canvas to help menu +7) {load,store} LRU {open,save} directory +8) operators: crop, scale, shift, slice +9) auto{load,save} & {backup,restore} +10) tools: unicode block elements +11) floating/dockable toolbar vim:ff=dos tw=0 diff --git a/libgui/GuiCanvasWxBackend.py b/libgui/GuiCanvasWxBackend.py index d5b54bc..156d0d7 100644 --- a/libgui/GuiCanvasWxBackend.py +++ b/libgui/GuiCanvasWxBackend.py @@ -70,65 +70,59 @@ class GuiCanvasWxBackend(): CS_ITALIC = 0x02 CS_UNDERLINE = 0x04 - def _drawBrushPatch(self, eventDc, patch, point): + def _drawBrushPatch(self, eventDc, isCursor, patch, point): absPoint = self._xlatePoint(point) - brushBg, brushFg, pen = self._getBrushPatchColours(patch) - self._setBrushDc(brushBg, brushFg, eventDc, pen) - eventDc.DrawRectangle(*absPoint, *self.cellSize) + dc = self._setBrushPatchColours(eventDc, isCursor, patch) + dc.DrawRectangle(*absPoint, *self.cellSize) - def _drawCharPatch(self, eventDc, patch, point): - absPoint, fontBitmap = self._xlatePoint(point), wx.Bitmap(*self.cellSize) - brushBg, brushFg, pen = self._getCharPatchColours(patch) - fontDc = wx.MemoryDC(); fontDc.SelectObject(fontBitmap); fontDc.SetFont(self._font); - fontDc.SetBackground(brushBg); fontDc.SetBrush(brushFg); fontDc.SetPen(pen); - fontDc.SetTextForeground(wx.Colour(Colours[patch[0]][:4])) - fontDc.SetTextBackground(wx.Colour(Colours[patch[1]][:4])) - fontDc.DrawRectangle(0, 0, *self.cellSize) - fontDc.SetPen(self._pens[patch[0]]) - if (patch[2] & self._CellState.CS_UNDERLINE) \ - or (patch[3] == "_"): - fontDc.DrawLine(0, self.cellSize[1] - 1, self.cellSize[0], self.cellSize[1] - 1) + def _drawCharPatch(self, eventDc, isCursor, patch, point): + absPoint = self._xlatePoint(point) + dc, pen = self._setCharPatchColours(eventDc, isCursor, patch) + dc.DrawRectangle(*absPoint, *self.cellSize) + if (patch[2] & self._CellState.CS_UNDERLINE) or (patch[3] == "_"): + dc.SetPen(self._pens[patch[0]]); + if not isCursor: + dc.DrawLine(absPoint[0], absPoint[1] + self.cellSize[1] - 1, absPoint[0] + self.cellSize[0] - 1, absPoint[1] + self.cellSize[1] - 1) + else: + dc.DrawLines((wx.Point2D(absPoint[0], absPoint[1] + self.cellSize[1] - 1), wx.Point2D(absPoint[0] + self.cellSize[0] - 1, absPoint[1] + self.cellSize[1] - 1),)) + dc.SetPen(pen) if patch[3] != "_": - fontDc.DrawText(patch[3], 0, 0) - eventDc.Blit(*absPoint, *self.cellSize, fontDc, 0, 0) + if not isCursor: + oldClippingRegion = dc.GetClippingBox() + dc.DestroyClippingRegion(); dc.SetClippingRegion(*absPoint, *self.cellSize); + dc.SetTextBackground(wx.Colour(Colours[patch[1]][:4])); dc.SetTextForeground(wx.Colour(Colours[patch[0]][:4])); + else: + dc.ResetClip(); dc.Clip(wx.Region(*absPoint, *self.cellSize)); + dc.DrawText(patch[3], *absPoint) + if not isCursor: + dc.DestroyClippingRegion() def _finiBrushesAndPens(self): [brush.Destroy() for brush in self._brushes or []] + [brushTransp.Destroy() for brushTransp in self._brushesTransp or []] [pen.Destroy() for pen in self._pens or []] - self._brushes, self._lastBrushBg, self._lastBrushFg, self._lastPen, self._pens = None, None, None, None, None - - def _getBrushPatchColours(self, patch): - if (patch[0] != -1) and (patch[1] != -1): - brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]] - elif (patch[0] == -1) and (patch[1] == -1): - brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha - elif patch[0] == -1: - brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]] - elif patch[1] == -1: - brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha - return (brushBg, brushFg, pen) - - def _getCharPatchColours(self, patch): - if (patch[0] != -1) and (patch[1] != -1): - brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]] - elif (patch[0] == -1) and (patch[1] == -1): - brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha - elif patch[0] == -1: - brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]] - elif patch[1] == -1: - brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha - return (brushBg, brushFg, pen) + [penTransp.Destroy() for penTransp in self._pensTransp or []] + self._brushAlpha.Destroy(); self._penAlpha.Destroy(); + self._brushAlphaTransp.Destroy(); self._penAlphaTransp.Destroy(); + self._brushes, self._lastBrush, self._lastPen, self._pens = None, None, None, None + self._brushesTransp, self._lastBrushTransp, self._lastPenTransp, self._pensTransp = None, None, None, None def _initBrushesAndPens(self): self._brushes, self._pens = [None for x in range(len(Colours))], [None for x in range(len(Colours))] + self._brushesTransp, self._pensTransp = [None for x in range(len(Colours))], [None for x in range(len(Colours))] for mircColour in range(len(Colours)): self._brushes[mircColour] = wx.Brush(wx.Colour(Colours[mircColour][:4]), wx.BRUSHSTYLE_SOLID) + self._brushesTransp[mircColour] = wx.Brush(wx.Colour(*Colours[mircColour][:3], 200), wx.BRUSHSTYLE_SOLID) self._pens[mircColour] = wx.Pen(wx.Colour(Colours[mircColour][:4]), 1) + self._pensTransp[mircColour] = wx.Pen(wx.Colour(*Colours[mircColour][:3], 200), 1, wx.PENSTYLE_TRANSPARENT) self._brushAlpha = wx.Brush(wx.Colour(Colours[14][:4]), wx.BRUSHSTYLE_SOLID) + self._brushAlphaTransp = wx.Brush(wx.Colour(*Colours[14][:3], 200), wx.BRUSHSTYLE_SOLID) self._penAlpha = wx.Pen(wx.Colour(Colours[14][:4]), 1) - self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None + self._penAlphaTransp = wx.Pen(wx.Colour(*Colours[14][:3], 200), 1, wx.PENSTYLE_TRANSPARENT) + self._lastBrush, self._lastPen = None, None + self._lastBrushTransp, self._lastPenTransp = None, None - def _reshapeArabic(self, canvas, eventDc, patch, point): + def _reshapeArabic(self, canvas, eventDc, isCursor, patch, point): lastCell = point[0] while True: if ((lastCell + 1) >= (canvas.size[0] - 1)) \ @@ -153,21 +147,55 @@ class GuiCanvasWxBackend(): runCell[3] = self.arabicShapes[runCell[3]][1]; connect = True; else: runCell[3] = self.arabicShapes[runCell[3]][0]; connect = False; - self._drawCharPatch(eventDc, runCell, [runX, point[1]]) + self._drawCharPatch(eventDc, isCursor, runCell, [runX, point[1]]) runCell = list(patch[2:]) if connect and (self.arabicShapes[patch[5]][3] != None): runCell[3] = self.arabicShapes[patch[5]][3] else: runCell[3] = self.arabicShapes[patch[5]][0] - self._drawCharPatch(eventDc, runCell, [point[0], point[1]]) + self._drawCharPatch(eventDc, isCursor, runCell, [point[0], point[1]]) - def _setBrushDc(self, brushBg, brushFg, dc, pen): - if self._lastBrushBg != brushBg: - dc.SetBackground(brushBg); self._lastBrushBg = brushBg; - if self._lastBrushFg != brushFg: - dc.SetBrush(brushFg); self._lastBrushFg = brushFg; - if self._lastPen != pen: - dc.SetPen(pen); self._lastPen = pen; + def _setBrushPatchColours(self, dc, isCursor, patch): + if not isCursor: + brushAlpha, brushes, dc_, penAlpha, pens = self._brushAlpha, self._brushes, dc, self._penAlpha, self._pens + else: + brushAlpha, brushes, dc_, penAlpha, pens = self._brushAlphaTransp, self._brushesTransp, wx.GraphicsContext.Create(dc), self._penAlphaTransp, self._pensTransp + if ((patch[0] != -1) and (patch[1] != -1)) \ + or ((patch[0] == -1) and (patch[1] != -1)): + brush, pen = brushes[patch[1]], pens[patch[1]] + else: + brush, pen = brushAlpha, penAlpha + if not isCursor: + if self._lastBrush != brush: + dc_.SetBrush(brush); self._lastBrush = brush; + if self._lastPen != pen: + dc_.SetPen(pen); self._lastPen = pen; + else: + dc_.SetBrush(brush); dc_.SetPen(pen); + return dc_ + + def _setCharPatchColours(self, dc, isCursor, patch): + if not isCursor: + brushAlpha, brushes, dc_, penAlpha, pens = self._brushAlpha, self._brushes, dc, self._penAlpha, self._pens + dc_.SetFont(self._font) + else: + brushAlpha, brushes, dc_, penAlpha, pens = self._brushAlphaTransp, self._brushesTransp, wx.GraphicsContext.Create(dc), self._penAlphaTransp, self._pensTransp + if (patch[0] != -1) and (patch[1] != -1): + brush, fontColour, pen = brushes[patch[1]], Colours[patch[0]][:3], pens[patch[1]] + elif (patch[0] == -1) and (patch[1] == -1): + brush, fontColour, pen = brushAlpha, Colours[14][:4], penAlpha + elif patch[0] == -1: + brush, fontColour, pen = brushes[patch[1]], Colours[14][:3], pens[patch[1]] + elif patch[1] == -1: + brush, fontColour, pen = brushAlpha, Colours[patch[0]][:3], penAlpha + if not isCursor: + if self._lastBrush != brush: + dc_.SetBrush(brush); self._lastBrush = brush; + if self._lastPen != pen: + dc_.SetPen(pen); self._lastPen = pen; + else: + dc_.SetBrush(brush); dc_.SetFont(self._font, wx.Colour(fontColour)); dc_.SetPen(pen); + return dc_, pen def _xlatePoint(self, point): return [a * b for a, b in zip(point, self.cellSize)] @@ -175,20 +203,20 @@ class GuiCanvasWxBackend(): def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc): [self.drawPatch(canvas, eventDc, patch) for patch in canvasJournal.popCursor()] - def drawPatch(self, canvas, eventDc, patch): + def drawPatch(self, canvas, eventDc, patch, isCursor=False): point = patch[:2] if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]: if patch[5] == " ": if patch[3] == -1: - self._drawCharPatch(eventDc, [*patch[2:-1], "░"], point) + self._drawCharPatch(eventDc, isCursor, [*patch[2:-1], "░"], point) elif patch[4] & self._CellState.CS_UNDERLINE: - self._drawCharPatch(eventDc, patch[2:], point) + self._drawCharPatch(eventDc, isCursor, patch[2:], point) else: - self._drawBrushPatch(eventDc, patch[2:], point) + self._drawBrushPatch(eventDc, isCursor, patch[2:], point) elif patch[5] in self.arabicShapes: - self._reshapeArabic(canvas, eventDc, patch, point) + self._reshapeArabic(canvas, eventDc, isCursor, patch, point) else: - self._drawCharPatch(eventDc, patch[2:], point) + self._drawCharPatch(eventDc, isCursor, patch[2:], point) return True else: return False @@ -200,7 +228,7 @@ class GuiCanvasWxBackend(): eventDc = wx.BufferedDC(wx.ClientDC(parentWindow), self.canvasBitmap) else: eventDc = GuiBufferedDC(self, self.canvasBitmap, clientSize, wx.ClientDC(parentWindow), viewRect) - self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None + self._lastBrush, self._lastPen = None, None return eventDc def onPaint(self, clientSize, panelWindow, viewRect): diff --git a/libroar/RoarCanvasCommands.py b/libroar/RoarCanvasCommands.py index 3696b05..f645c47 100644 --- a/libroar/RoarCanvasCommands.py +++ b/libroar/RoarCanvasCommands.py @@ -24,7 +24,7 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan toolBitmapBrush = wx.Brush(wx.Colour([*[int(c / div) for c in toolBitmapColour[:3]], 255]), wx.BRUSHSTYLE_SOLID) toolBitmapDc.SetBrush(toolBitmapBrush) toolBitmapDc.SetBackground(toolBitmapBrush) - toolBitmapDc.SetPen(wx.Pen(wx.Colour(toolBitmapColour), 1)) + toolBitmapDc.SetPen(wx.Pen(toolBitmapColour, 1)) toolBitmapDc.DrawRectangle(0, 0, 16, 16) cmd.attrList[numColour]["icon"] = ["", None, toolBitmap] toolBitmapColours = ((0, 0, 0, 255), (255, 255, 255, 255)) @@ -38,7 +38,7 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan toolBitmapDc.DrawRectangle(8, 8, 16, 16) cmdAlpha.attrList[0]["icon"] = ["", None, toolBitmap] _initColourBitmaps_(RoarCanvasCommandsEdit.canvasColour, RoarCanvasCommandsEdit.canvasColourAlpha, 1.0) - _initColourBitmaps_(RoarCanvasCommandsEdit.canvasColourBackground, RoarCanvasCommandsEdit.canvasColourAlphaBackground, 0.5) + _initColourBitmaps_(RoarCanvasCommandsEdit.canvasColourBackground, RoarCanvasCommandsEdit.canvasColourAlphaBackground, 1.5) def update(self, **kwargs): self.lastPanelState.update(kwargs); textItems = []; diff --git a/libroar/RoarCanvasWindow.py b/libroar/RoarCanvasWindow.py index 40c7096..fbc45e4 100644 --- a/libroar/RoarCanvasWindow.py +++ b/libroar/RoarCanvasWindow.py @@ -44,7 +44,7 @@ class RoarCanvasWindow(GuiWindow): if not self.canvas.dirtyCursor: self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc) self.canvas.dirtyCursor = True - if self.backend.drawPatch(self.canvas, eventDc, patch) and isCursor: + if self.backend.drawPatch(self.canvas, eventDc, patch, isCursor=isCursor) and isCursor: patchDeltaCell = self.canvas.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell]; self.canvas.journal.pushCursor(patchDelta) diff --git a/libtools/ToolLine.py b/libtools/ToolLine.py index 52ee5a2..2603ba5 100644 --- a/libtools/ToolLine.py +++ b/libtools/ToolLine.py @@ -23,18 +23,21 @@ class ToolLine(Tool): lineXX, lineXY, lineYX, lineYY = 0, lineYSign, lineXSign, 0 pointDelta = [pointDelta[1], pointDelta[0]] lineD = 2 * pointDelta[1] - pointDelta[0]; lineY = 0; + pointsDone = [] for lineX in range(pointDelta[0] + 1): for brushStep in range(brushSize[0]): - patch = [ \ - originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, \ - originPoint[1] + lineX * lineXY + lineY * lineYY, \ - *brushColours, 0, " "] - if isCursor: - dispatchFn(eventDc, False, patch); dispatchFn(eventDc, True, patch); - else: - if not dirty: - dirty = True - dispatchFn(eventDc, True, patch) + if not ([originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, originPoint[1] + lineX * lineXY + lineY * lineYY] in pointsDone): + patch = [ \ + originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, \ + originPoint[1] + lineX * lineXY + lineY * lineYY, \ + *brushColours, 0, " "] + if not isCursor: + if not dirty: + dirty = True + dispatchFn(eventDc, False, patch) + else: + dispatchFn(eventDc, True, patch) + pointsDone += [[originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, originPoint[1] + lineX * lineXY + lineY * lineYY]] if lineD > 0: lineD -= pointDelta[0]; lineY += 1; lineD += pointDelta[1] @@ -58,13 +61,15 @@ class ToolLine(Tool): brushColours[1] = brushColours[0] if self.toolState == self.TS_NONE: if mouseLeftDown or mouseRightDown: - self.toolColours, self.toolOriginPoint, self.toolState = brushColours, list(mapPoint), self.TS_ORIGIN + self.toolOriginPoint, self.toolState = list(mapPoint), self.TS_ORIGIN dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "]) elif self.toolState == self.TS_ORIGIN: originPoint, targetPoint = self.toolOriginPoint, list(mapPoint) - dirty = self._getLine(self.toolColours, brushSize, dispatchFn, eventDc, mouseLeftDown or mouseRightDown, originPoint, targetPoint) if mouseLeftDown or mouseRightDown: - self.toolColours, self.toolOriginPoint, self.toolState = None, None, self.TS_NONE + dirty = self._getLine(brushColours, brushSize, dispatchFn, eventDc, False, originPoint, targetPoint) + self.toolOriginPoint, self.toolState = None, self.TS_NONE + else: + dirty = self._getLine(brushColours, brushSize, dispatchFn, eventDc, True, originPoint, targetPoint) else: return False, dirty return True, dirty @@ -72,6 +77,6 @@ class ToolLine(Tool): # __init__(self, *args): initialisation method def __init__(self, *args): super().__init__(*args) - self.toolColours, self.toolOriginPoint, self.toolState = None, None, self.TS_NONE + self.toolOriginPoint, self.toolState = None, self.TS_NONE # vim:expandtab foldmethod=marker sw=4 ts=4 tw=120