diff --git a/jssrc/term_screen.js b/jssrc/term_screen.js index 689be1c..6eba873 100644 --- a/jssrc/term_screen.js +++ b/jssrc/term_screen.js @@ -593,14 +593,20 @@ class TermScreen { return [ x * cellSize.width, y * cellSize.height ] } - drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs }) { + drawCellBackground ({ x, y, cellWidth, cellHeight, bg }) { const ctx = this.ctx ctx.fillStyle = this.getColor(bg) + ctx.clearRect(x * cellWidth, y * cellHeight, + Math.ceil(cellWidth), Math.ceil(cellHeight)) ctx.fillRect(x * cellWidth, y * cellHeight, Math.ceil(cellWidth), Math.ceil(cellHeight)) + } + drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs }) { if (!text) return + const ctx = this.ctx + let underline = false let blink = false let strike = false @@ -647,6 +653,22 @@ class TermScreen { ctx.globalAlpha = 1 } + getAdjacentCells (cell) { + const { width, height } = this.window + const screenLength = width * height + + return [ + cell - 1, + cell + 1, + cell - width, + cell + width, + cell - width - 1, + cell - width + 1, + cell + width - 1, + cell + width + 1 + ].filter(adjacentCell => adjacentCell >= 0 && adjacentCell < screenLength) + } + draw () { const ctx = this.ctx const { @@ -675,7 +697,7 @@ class TermScreen { // Map of (attrs & FONT_MASK) -> Array of cell indices const fontGroups = new Map() - // Map of (cell index) -> boolean, whether or not a cell needs to be redrawn + // Map of (cell index) -> boolean, whether or not a cell has updated const updateMap = new Map() for (let cell = 0; cell < screenLength; cell++) { @@ -703,7 +725,7 @@ class TermScreen { bg = -2 } - let needsUpdate = text !== this.drawnScreen[cell] || + let didUpdate = text !== this.drawnScreen[cell] || fg !== this.drawnScreenFG[cell] || bg !== this.drawnScreenBG[cell] || attrs !== this.drawnScreenAttrs[cell] || @@ -713,9 +735,78 @@ class TermScreen { if (!fontGroups.has(font)) fontGroups.set(font, []) fontGroups.get(font).push([cell, x, y, text, fg, bg, attrs, isCursor]) - updateMap.set(cell, needsUpdate) + updateMap.set(cell, didUpdate) } + // Map of (cell index) -> boolean, whether or not a cell should be redrawn + const redrawMap = new Map() + + // decide for each cell if it should be redrawn + let updateRedrawMapAt = cell => { + let shouldUpdate = updateMap.get(cell) || redrawMap.get(cell) + let adjacentCells = this.getAdjacentCells(cell) + + if (!shouldUpdate) { + // check adjacent cells + let adjacentDidUpdate = false + + for (let adjacentCell of adjacentCells) { + if (updateMap.get(adjacentCell)) { + adjacentDidUpdate = true + break + } + } + + if (adjacentDidUpdate) shouldUpdate = true + } + + if (shouldUpdate) { + // TODO: fonts (necessary?) + let text = this.screen[cell] + let isWideCell = ctx.measureText(text).width >= cellWidth + if (text === ' ') isWideCell = false + + if (isWideCell) { + // set redraw for adjacent cells + for (let adjacentCell of adjacentCells) { + redrawMap.set(adjacentCell, true) + } + + // update previous wide cells as well + let index = cell - 1 + while (index > 0) { + let text = this.screen[index] + let isWide = ctx.measureText(text).width >= cellWidth && text !== ' ' + + if (redrawMap.get(index - 1)) break + + // might be out of bounds but that doesn't matter + redrawMap.set(index - width, true) + redrawMap.set(index + width, true) + redrawMap.set(index--, true) + + if (!isWide) break + } + } + } + + redrawMap.set(cell, shouldUpdate) + } + + for (let cell of updateMap.keys()) updateRedrawMapAt(cell) + + // pass 1: backgrounds + for (let font of fontGroups.keys()) { + for (let data of fontGroups.get(font)) { + let [cell, x, y, text, fg, bg, attrs, isCursor] = data + + if (redrawMap.get(cell)) { + this.drawCellBackground({ x, y, cellWidth, cellHeight, bg }) + } + } + } + + // pass 2: characters for (let font of fontGroups.keys()) { // set font once because in Firefox, this is a really slow action for some // reason @@ -727,28 +818,7 @@ class TermScreen { for (let data of fontGroups.get(font)) { let [cell, x, y, text, fg, bg, attrs, isCursor] = data - // check if this cell or any adjacent cells updated - let needsUpdate = false - let updateCells = [ - cell, - cell - 1, - cell + 1, - cell - width, - cell + width, - // diagonal box drawing characters exist, too - cell - width - 1, - cell - width + 1, - cell + width - 1, - cell + width + 1 - ] - for (let index of updateCells) { - if (updateMap.has(index) && updateMap.get(index)) { - needsUpdate = true - break - } - } - - if (needsUpdate) { + if (redrawMap.get(cell)) { this.drawCell({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs })