Only redraw changed cells (huge performance boost!)

also add semicolons
http-comm
cpsdqs 7 years ago
parent 3e6187dba9
commit 5cc3003e47
Signed by untrusted user: cpsdqs
GPG Key ID: 3F59586BB7448DD1
  1. 139
      html_orig/jssrc/term_screen.js

@ -126,6 +126,12 @@ class TermScreen {
this.screenBG = [];
this.screenAttrs = [];
// used to determine if a cell should be redrawn
this.drawnScreen = [];
this.drawnScreenFG = [];
this.drawnScreenBG = [];
this.drawnScreenAttrs = [];
this.resetBlink();
this.resetCursorBlink();
@ -417,6 +423,12 @@ class TermScreen {
this.canvas.height = height * devicePixelRatio * cellSize.height;
this.canvas.style.height = `${realHeight}px`;
// the screen has been cleared (by changing canvas width)
this.drawnScreen = [];
this.drawnScreenFG = [];
this.drawnScreenBG = [];
this.drawnScreenAttrs = [];
// draw immediately; the canvas shouldn't flash
this.draw();
}
@ -427,8 +439,8 @@ class TermScreen {
clearInterval(this.cursor.blinkInterval);
this.cursor.blinkInterval = setInterval(() => {
this.cursor.blinkOn = !this.cursor.blinkOn;
this.scheduleDraw()
}, 500)
this.scheduleDraw();
}, 500);
}
resetBlink () {
@ -439,12 +451,12 @@ class TermScreen {
intervals++;
if (intervals >= 4 && this.window.blinkStyleOn) {
this.window.blinkStyleOn = false;
intervals = 0
intervals = 0;
} else if (intervals >= 1 && !this.window.blinkStyleOn) {
this.window.blinkStyleOn = true;
intervals = 0
intervals = 0;
}
}, 200)
}, 200);
}
getNormalizedSelection () {
@ -452,9 +464,9 @@ class TermScreen {
// if the start line is after the end line, or if they're both on the same
// line but the start column comes after the end column, swap
if (start[1] > end[1] || (start[1] === end[1] && start[0] > end[0])) {
[start, end] = [end, start]
[start, end] = [end, start];
}
return { start, end }
return { start, end };
}
isInSelection (col, line) {
@ -467,7 +479,7 @@ class TermScreen {
if (onStartLine && onEndLine) return colAfterStart && colBeforeEnd;
else if (onStartLine) return colAfterStart;
else if (onEndLine) return colBeforeEnd;
else return start[1] < line && line < end[1]
else return start[1] < line && line < end[1];
}
getSelectedText () {
@ -482,13 +494,13 @@ class TermScreen {
if (this.isInSelection(x, y)) {
if (previousLineIndex !== y) {
previousLineIndex = y;
lines.push('')
lines.push('');
}
lines[lines.length - 1] += this.screen[cell]
lines[lines.length - 1] += this.screen[cell];
}
}
return lines.join('\n')
return lines.join('\n');
}
copySelectionToClipboard () {
@ -513,7 +525,7 @@ class TermScreen {
return [
Math.floor((x + cellSize.width / 2) / cellSize.width),
Math.floor(y / cellSize.height)
Math.floor(y / cellSize.height),
];
}
@ -523,15 +535,12 @@ class TermScreen {
return [ x * cellSize.width, y * cellSize.height ];
}
drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs },
compositeAbove = false) {
drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs }) {
const ctx = this.ctx;
const inSelection = this.isInSelection(x, y);
ctx.fillStyle = inSelection ? SELECTION_BG : this.colors[bg];
if (!compositeAbove) ctx.globalCompositeOperation = 'destination-over';
ctx.fillRect(x * cellWidth, y * cellHeight,
Math.ceil(cellWidth), Math.ceil(cellHeight));
ctx.globalCompositeOperation = 'source-over';
if (!text) return;
@ -566,11 +575,11 @@ class TermScreen {
ctx.lineTo((x + 1) * cellWidth, lineY);
}
ctx.stroke()
ctx.stroke();
}
}
ctx.globalAlpha = 1
ctx.globalAlpha = 1;
}
draw () {
@ -590,7 +599,6 @@ class TermScreen {
const screenLength = width * height;
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);
ctx.clearRect(0, 0, screenWidth, screenHeight);
ctx.font = this.getFont();
ctx.textAlign = 'center';
@ -602,40 +610,79 @@ 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
const updateMap = new Map();
for (let cell = 0; cell < screenLength; cell++) {
let x = cell % width;
let y = Math.floor(cell / width);
let isCursor = this.cursor.x === x && this.cursor.y === y &&
!this.cursor.hanging;
let invertForCursor = isCursor && this.cursor.blinkOn &&
this.cursor.style === 'block';
let text = this.screen[cell];
let fg = invertForCursor ? this.screenBG[cell] : this.screenFG[cell];
let bg = invertForCursor ? this.screenFG[cell] : this.screenBG[cell];
let attrs = this.screenAttrs[cell];
// HACK: ensure cursor is visible
if (invertForCursor && fg === bg) bg = fg === 0 ? 7 : 0;
let cellDidChange = text !== this.drawnScreen[cell] ||
fg !== this.drawnScreenFG[cell] ||
bg !== this.drawnScreenBG[cell] ||
attrs !== this.drawnScreenAttrs[cell];
let font = attrs & FONT_MASK;
if (!fontGroups.has(font)) fontGroups.set(font, []);
fontGroups.get(font).push(cell);
fontGroups.get(font).push([cell, x, y, text, fg, bg, attrs, isCursor]);
updateMap.set(cell, cellDidChange);
}
for (let font of fontGroups.keys()) {
// set font once because in Firefox, this is a really slow action for some
// reason
let modifiers = {}
if (font & 1) modifiers.weight = 'bold'
if (font & 1 << 2) modifiers.style = 'italic'
ctx.font = this.getFont(modifiers)
for (let cell of fontGroups.get(font)) {
let x = cell % width;
let y = Math.floor(cell / width);
let isCursor = this.cursor.x === x && this.cursor.y === y;
if (this.cursor.hanging) isCursor = false;
let invertForCursor = isCursor && this.cursor.blinkOn &&
this.cursor.style === 'block';
let text = this.screen[cell];
let fg = invertForCursor ? this.screenBG[cell] : this.screenFG[cell];
let bg = invertForCursor ? this.screenFG[cell] : this.screenBG[cell];
let attrs = this.screenAttrs[cell];
// HACK: ensure cursor is visible
if (invertForCursor && fg === bg) bg = fg === 0 ? 7 : 0;
this.drawCell({
x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs
});
let modifiers = {};
if (font & 1) modifiers.weight = 'bold';
if (font & 1 << 2) modifiers.style = 'italic';
ctx.font = this.getFont(modifiers);
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) {
this.drawCell({
x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs
});
this.drawnScreen[cell] = text;
this.drawnScreenFG[cell] = fg;
this.drawnScreenBG[cell] = bg;
this.drawnScreenAttrs[cell] = attrs;
}
if (isCursor && this.cursor.blinkOn && this.cursor.style !== 'block') {
ctx.save();
@ -660,8 +707,8 @@ class TermScreen {
this.drawCell({
x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs
}, true);
ctx.restore()
});
ctx.restore();
}
}
}

Loading…
Cancel
Save