From 45766fcccece62e942d7ba7e47e78804fc1cdaa7 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Thu, 9 Nov 2017 23:58:58 +0100 Subject: [PATCH 1/7] Add renderer support for double-size lines --- js/term/screen_renderer.js | 41 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/js/term/screen_renderer.js b/js/term/screen_renderer.js index 1f61760..8ee77b4 100644 --- a/js/term/screen_renderer.js +++ b/js/term/screen_renderer.js @@ -60,6 +60,7 @@ module.exports = class CanvasRenderer extends EventEmitter { this.screenBG = [] this.screenAttrs = [] this.screenSelection = [] + this.screenLines = [] this.cursor = {} this.reverseVideo = false this.hasBlinkingCells = false @@ -96,6 +97,7 @@ module.exports = class CanvasRenderer extends EventEmitter { this.drawnScreenFG = [] this.drawnScreenBG = [] this.drawnScreenAttrs = [] + this.drawnScreenLines = [] this.drawnCursor = [-1, -1, '', false] } @@ -209,6 +211,9 @@ module.exports = class CanvasRenderer extends EventEmitter { drawBackground ({ x, y, cellWidth, cellHeight, bg, isDefaultBG }) { const { ctx, width, height, padding } = this + // is a double-width/double-height line + if (this.screenLines[y]) cellWidth *= 2 + ctx.fillStyle = this.getColor(bg) let screenX = x * cellWidth + padding let screenY = y * cellHeight + padding @@ -276,6 +281,34 @@ module.exports = class CanvasRenderer extends EventEmitter { let screenX = x * cellWidth + padding let screenY = y * cellHeight + padding + if (this.screenLines[y]) { + // is a double-width/double-height line + cellWidth *= 2 + ctx.save() + ctx.translate(screenX + 0.5 * cellWidth, screenY + 0.5 * cellHeight) + ctx.scale(2, 1) + if (this.screenLines[y] & 0b10) { + // top half + ctx.scale(1, 2) + ctx.translate(0, cellHeight / 4) + + } else if (this.screenLines[y] & 0b100) { + // bottom half + ctx.scale(1, 2) + ctx.translate(0, -cellHeight / 4) + } + ctx.translate((-screenX - 1.5 * cellWidth) / 2, -screenY - 0.5 * cellHeight) + + if (this.screenLines[y] & 0b110) { + // characters overflow -- needs clipping + // TODO: clipping is really expensive + ctx.beginPath() + if (this.screenLines[y] & 0b10) ctx.rect(screenX, screenY, cellWidth, cellHeight / 2) + else ctx.rect(screenX, screenY + cellHeight / 2, cellWidth, cellHeight / 2) + ctx.clip() + } + } + let codePoint = text.codePointAt(0) if (codePoint >= 0x2580 && codePoint <= 0x259F) { // block elements @@ -450,6 +483,8 @@ module.exports = class CanvasRenderer extends EventEmitter { ctx.stroke() } + if (this.screenLines[y]) ctx.restore() + ctx.globalAlpha = 1 } @@ -558,6 +593,7 @@ module.exports = class CanvasRenderer extends EventEmitter { fg !== this.drawnScreenFG[cell] || // foreground updated, and this cell has text bg !== this.drawnScreenBG[cell] || // background updated attrs !== this.drawnScreenAttrs[cell] || // attributes updated + this.screenLines[y] !== this.drawnScreenLines[y] || // line updated // TODO: fix artifacts or keep this hack: isCursor || wasCursor || // cursor blink/position updated (isCursor && this.cursor.style !== this.drawnCursor[2]) || // cursor style updated @@ -570,6 +606,9 @@ module.exports = class CanvasRenderer extends EventEmitter { updateMap.set(cell, didUpdate) } + // set drawn screen lines + this.drawnScreenLines = this.screenLines.slice() + let debugFilledUpdates = [] if (this.graphics >= 1) { @@ -680,8 +719,6 @@ module.exports = class CanvasRenderer extends EventEmitter { i++ } - console.log(regions) - ctx.save() ctx.beginPath() for (let region of regions) { From 4e9970300b22c9245a869460c8449cbb42316709 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Fri, 10 Nov 2017 00:03:27 +0100 Subject: [PATCH 2/7] Add layout support for double size lines --- js/term/screen.js | 3 +++ js/term/screen_layout.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/js/term/screen.js b/js/term/screen.js index 47e644f..5567997 100644 --- a/js/term/screen.js +++ b/js/term/screen.js @@ -90,6 +90,7 @@ module.exports = class TermScreen extends EventEmitter { this.screenFG = [] this.screenBG = [] this.screenAttrs = [] + this.screenLines = [] let selecting = false @@ -266,6 +267,7 @@ module.exports = class TermScreen extends EventEmitter { this.screen.screenFG = new Array(width * height).fill(0) this.screen.screenBG = new Array(width * height).fill(0) this.screen.screenAttrs = new Array(width * height).fill(0) + this.screen.screenLines = new Array(height).fill(0) } updateLayout () { @@ -288,6 +290,7 @@ module.exports = class TermScreen extends EventEmitter { screenBG: this.screenBG, screenSelection: selection, screenAttrs: this.screenAttrs, + screenLines: this.screenLines, cursor: this.cursor, statusScreen: this.window.statusScreen, reverseVideo: this.reverseVideo, diff --git a/js/term/screen_layout.js b/js/term/screen_layout.js index 5a8f214..e78a1f6 100644 --- a/js/term/screen_layout.js +++ b/js/term/screen_layout.js @@ -135,8 +135,9 @@ module.exports = class ScreenLayout extends EventEmitter { x = x / this._windowScale - this._padding y = y / this._windowScale - this._padding - x = Math.floor((x + (rounded ? cellSize.width / 2 : 0)) / cellSize.width) y = Math.floor(y / cellSize.height) + if (this.renderer.drawnScreenLines[y]) x /= 2 // double size + x = Math.floor((x + (rounded ? cellSize.width / 2 : 0)) / cellSize.width) x = Math.max(0, Math.min(this.window.width - 1, x)) y = Math.max(0, Math.min(this.window.height - 1, y)) @@ -153,6 +154,8 @@ module.exports = class ScreenLayout extends EventEmitter { gridToScreen (x, y, withScale = false) { let cellSize = this.getCellSize() + if (this.renderer.drawnScreenLines[y]) x *= 2 // double size + return [x * cellSize.width, y * cellSize.height].map(v => this._padding + (withScale ? v * this._windowScale : v)) } From f61d861883c59a6a79efd740c0a3ed0784d51ebc Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Fri, 10 Nov 2017 00:09:12 +0100 Subject: [PATCH 3/7] Make cursor work in double-width lines --- js/term/screen_renderer.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/js/term/screen_renderer.js b/js/term/screen_renderer.js index 8ee77b4..668431d 100644 --- a/js/term/screen_renderer.js +++ b/js/term/screen_renderer.js @@ -800,17 +800,22 @@ module.exports = class CanvasRenderer extends EventEmitter { let cursorX = x let cursorY = y + let cursorWidth = cellWidth // JS doesn't allow same-name assignment if (this.cursor.hanging) { // draw hanging cursor in the margin cursorX += 1 } - let screenX = cursorX * cellWidth + this.padding + // double-width lines + if (this.screenLines[cursorY]) cursorWidth *= 2 + + let screenX = cursorX * cursorWidth + this.padding let screenY = cursorY * cellHeight + this.padding + if (this.cursor.style === 'block') { // block - ctx.rect(screenX, screenY, cellWidth, cellHeight) + ctx.rect(screenX, screenY, cursorWidth, cellHeight) } else if (this.cursor.style === 'bar') { // vertical bar let barWidth = 2 @@ -818,7 +823,7 @@ module.exports = class CanvasRenderer extends EventEmitter { } else if (this.cursor.style === 'line') { // underline let lineHeight = 2 - ctx.rect(screenX, screenY + charSize.height, cellWidth, lineHeight) + ctx.rect(screenX, screenY + charSize.height, cursorWidth, lineHeight) } ctx.clip() From d489e9919435b602417bc5bf0e65391230cae73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 12 Nov 2017 09:42:10 +0100 Subject: [PATCH 4/7] fixed double height always being also double width --- js/term/screen.js | 7 +++++++ js/term/screen_renderer.js | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/js/term/screen.js b/js/term/screen.js index 5567997..7985eec 100644 --- a/js/term/screen.js +++ b/js/term/screen.js @@ -92,6 +92,13 @@ module.exports = class TermScreen extends EventEmitter { this.screenAttrs = [] this.screenLines = [] + // For testing TODO remove + this.screenLines[0] = 0b001 + this.screenLines[1] = 0b010 + this.screenLines[2] = 0b100 + this.screenLines[3] = 0b011 + this.screenLines[4] = 0b101 + let selecting = false let selectStart = (x, y) => { diff --git a/js/term/screen_renderer.js b/js/term/screen_renderer.js index 668431d..7c1b815 100644 --- a/js/term/screen_renderer.js +++ b/js/term/screen_renderer.js @@ -212,7 +212,7 @@ module.exports = class CanvasRenderer extends EventEmitter { const { ctx, width, height, padding } = this // is a double-width/double-height line - if (this.screenLines[y]) cellWidth *= 2 + if (this.screenLines[y] & 0b001) cellWidth *= 2 ctx.fillStyle = this.getColor(bg) let screenX = x * cellWidth + padding @@ -281,29 +281,34 @@ module.exports = class CanvasRenderer extends EventEmitter { let screenX = x * cellWidth + padding let screenY = y * cellHeight + padding + const dblWidth = this.screenLines[y] & 0b001 + const dblHeightTop = this.screenLines[y] & 0b010 + const dblHeightBot = this.screenLines[y] & 0b100 + if (this.screenLines[y]) { // is a double-width/double-height line - cellWidth *= 2 + if (dblWidth) cellWidth *= 2 + ctx.save() ctx.translate(screenX + 0.5 * cellWidth, screenY + 0.5 * cellHeight) - ctx.scale(2, 1) - if (this.screenLines[y] & 0b10) { + if (dblWidth) ctx.scale(2, 1) + if (dblHeightTop) { // top half ctx.scale(1, 2) ctx.translate(0, cellHeight / 4) - } else if (this.screenLines[y] & 0b100) { + } else if (dblHeightBot) { // bottom half ctx.scale(1, 2) ctx.translate(0, -cellHeight / 4) } ctx.translate((-screenX - 1.5 * cellWidth) / 2, -screenY - 0.5 * cellHeight) - if (this.screenLines[y] & 0b110) { + if (dblHeightBot || dblHeightTop) { // characters overflow -- needs clipping // TODO: clipping is really expensive ctx.beginPath() - if (this.screenLines[y] & 0b10) ctx.rect(screenX, screenY, cellWidth, cellHeight / 2) + if (dblHeightTop) ctx.rect(screenX, screenY, cellWidth, cellHeight / 2) else ctx.rect(screenX, screenY + cellHeight / 2, cellWidth, cellHeight / 2) ctx.clip() } From 0893b0a26886f64c0fa456a4cc69fef0992d7ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 12 Nov 2017 10:20:04 +0100 Subject: [PATCH 5/7] added update topic for doublelines --- js/term/screen.js | 15 ++++++++++----- js/term/screen_parser.js | 30 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/js/term/screen.js b/js/term/screen.js index 7985eec..ee3e448 100644 --- a/js/term/screen.js +++ b/js/term/screen.js @@ -93,11 +93,11 @@ module.exports = class TermScreen extends EventEmitter { this.screenLines = [] // For testing TODO remove - this.screenLines[0] = 0b001 - this.screenLines[1] = 0b010 - this.screenLines[2] = 0b100 - this.screenLines[3] = 0b011 - this.screenLines[4] = 0b101 + // this.screenLines[0] = 0b001 + // this.screenLines[1] = 0b010 + // this.screenLines[2] = 0b100 + // this.screenLines[3] = 0b011 + // this.screenLines[4] = 0b101 let selecting = false @@ -500,6 +500,11 @@ module.exports = class TermScreen extends EventEmitter { this.emit('opts-update') break + case 'double-lines': + this.screenLines = update.lines + this.renderScreen('double-lines') + break + case 'static-opts': this.layout.window.fontFamily = update.fontStack || null this.layout.window.fontSize = update.fontSize diff --git a/js/term/screen_parser.js b/js/term/screen_parser.js index c3a8546..9773dbc 100644 --- a/js/term/screen_parser.js +++ b/js/term/screen_parser.js @@ -27,15 +27,17 @@ function du (str) { } /* eslint-disable no-multi-spaces */ -const TOPIC_SCREEN_OPTS = 'O' -const TOPIC_STATIC_OPTS = 'P' -const TOPIC_CONTENT = 'S' -const TOPIC_TITLE = 'T' -const TOPIC_BUTTONS = 'B' -const TOPIC_CURSOR = 'C' -const TOPIC_INTERNAL = 'D' -const TOPIC_BELL = '!' -const TOPIC_BACKDROP = 'W' +// mnemonic +const TOPIC_SCREEN_OPTS = 'O' // O-ptions +const TOPIC_STATIC_OPTS = 'P' // P-arams +const TOPIC_CONTENT = 'S' // S-creen +const TOPIC_TITLE = 'T' // T-itle +const TOPIC_BUTTONS = 'B' // B-uttons +const TOPIC_CURSOR = 'C' // C-ursor +const TOPIC_INTERNAL = 'D' // D-ebug +const TOPIC_BELL = '!' // !!! +const TOPIC_BACKDROP = 'W' // W-allpaper +const TOPIC_DOUBLE_LINES = 'H' // H-uge const OPT_CURSOR_VISIBLE = (1 << 0) const OPT_DEBUGBAR = (1 << 1) @@ -188,6 +190,16 @@ module.exports = class ScreenParser { fontSize }) + } else if (topic === TOPIC_DOUBLE_LINES) { + let lines = [] + const count = du(strArray[ci++]) + for (let i = 0; i < count; i++) { + // format: INDEX<<3 | (dbl-h-bot : dbl-h-top : dbl-w) + let n = du(strArray[ci++]) + lines[n >> 3] = n & 0b111 + } + updates.push({ topic: 'double-lines', lines: lines }) + } else if (topic === TOPIC_TITLE) { updates.push({ topic: 'title', title: collectOneTerminatedString() }) From 915113a628f26483d048b64f5ea74088febcf9ba Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Sun, 12 Nov 2017 12:16:31 +0100 Subject: [PATCH 6/7] Fix incorrect rendering of double-sized lines --- js/term/screen_renderer.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/js/term/screen_renderer.js b/js/term/screen_renderer.js index 7c1b815..7fe476f 100644 --- a/js/term/screen_renderer.js +++ b/js/term/screen_renderer.js @@ -296,13 +296,12 @@ module.exports = class CanvasRenderer extends EventEmitter { // top half ctx.scale(1, 2) ctx.translate(0, cellHeight / 4) - } else if (dblHeightBot) { // bottom half ctx.scale(1, 2) ctx.translate(0, -cellHeight / 4) } - ctx.translate((-screenX - 1.5 * cellWidth) / 2, -screenY - 0.5 * cellHeight) + ctx.translate((-screenX - (dblWidth ? 1.5 : 0.5) * cellWidth) / (dblWidth ? 2 : 1), -screenY - 0.5 * cellHeight) if (dblHeightBot || dblHeightTop) { // characters overflow -- needs clipping @@ -657,7 +656,10 @@ module.exports = class CanvasRenderer extends EventEmitter { // update this cell if: // - the adjacent cell updated (For now, this'll always be true because characters can be slightly larger than they say they are) // - the adjacent cell updated and this cell or the adjacent cell is wide - if (updateMap.get(adjacentCell) && (this.graphics < 2 || isWideCell || isTextWide(this.screen[adjacentCell]))) { + // - this or the adjacent cell is not double-sized + if (updateMap.get(adjacentCell) && + (this.graphics < 2 || isWideCell || isTextWide(this.screen[adjacentCell])) && + (!this.screenLines[Math.floor(cell / this.width)] && !this.screenLines[Math.floor(adjacentCell / this.width)])) { adjacentDidUpdate = true if (this.getAdjacentCells(cell, 1).includes(adjacentCell)) { @@ -813,7 +815,7 @@ module.exports = class CanvasRenderer extends EventEmitter { } // double-width lines - if (this.screenLines[cursorY]) cursorWidth *= 2 + if (this.screenLines[cursorY] & 0b001) cursorWidth *= 2 let screenX = cursorX * cursorWidth + this.padding let screenY = cursorY * cellHeight + this.padding From 681685fa3ba8ca5d4beae70995604fd28546e743 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Sun, 12 Nov 2017 12:43:49 +0100 Subject: [PATCH 7/7] Stop using weird transforms for double-width --- js/term/screen_renderer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/term/screen_renderer.js b/js/term/screen_renderer.js index 7fe476f..a5482a5 100644 --- a/js/term/screen_renderer.js +++ b/js/term/screen_renderer.js @@ -290,7 +290,7 @@ module.exports = class CanvasRenderer extends EventEmitter { if (dblWidth) cellWidth *= 2 ctx.save() - ctx.translate(screenX + 0.5 * cellWidth, screenY + 0.5 * cellHeight) + ctx.translate(padding, screenY + 0.5 * cellHeight) if (dblWidth) ctx.scale(2, 1) if (dblHeightTop) { // top half @@ -301,7 +301,8 @@ module.exports = class CanvasRenderer extends EventEmitter { ctx.scale(1, 2) ctx.translate(0, -cellHeight / 4) } - ctx.translate((-screenX - (dblWidth ? 1.5 : 0.5) * cellWidth) / (dblWidth ? 2 : 1), -screenY - 0.5 * cellHeight) + ctx.translate(-padding, -screenY - 0.5 * cellHeight) + if (dblWidth) ctx.translate(-cellWidth / 4, 0) if (dblHeightBot || dblHeightTop) { // characters overflow -- needs clipping