Fix DynamicCharAtlas with transparency

There were two bugs:
- We need to clear the background of _tmpCtx instead of just drawing on
  top if the background color is transparent.
- We should set the background to fully transparent if it's partially
  transparent, to avoid drawing the transparent background twice.
This commit is contained in:
Benjamin Woodruff
2018-04-15 19:43:23 -07:00
parent ac7db87fd0
commit 9c0ce1d742
4 changed files with 39 additions and 5 deletions
+3
View File
@@ -26,6 +26,9 @@
<p>
<label><input type="checkbox" id="option-mac-option-is-meta"> macOptionIsMeta</label>
</p>
<p>
<label><input type="checkbox" id="option-transparency"> transparency</label>
</p>
<p>
<label>
cursorStyle
+9 -3
View File
@@ -31,6 +31,7 @@ var terminalContainer = document.getElementById('terminal-container'),
cursorStyle: document.querySelector('#option-cursor-style'),
macOptionIsMeta: document.querySelector('#option-mac-option-is-meta'),
scrollback: document.querySelector('#option-scrollback'),
transparency: document.querySelector('#option-transparency'),
tabstopwidth: document.querySelector('#option-tabstopwidth'),
experimentalCharAtlas: document.querySelector('#option-experimental-char-atlas'),
bellStyle: document.querySelector('#option-bell-style'),
@@ -75,15 +76,20 @@ actionElements.findPrevious.addEventListener('keypress', function (e) {
optionElements.cursorBlink.addEventListener('change', function () {
term.setOption('cursorBlink', optionElements.cursorBlink.checked);
});
optionElements.macOptionIsMeta.addEventListener('change', function () {
term.setOption('macOptionIsMeta', optionElements.macOptionIsMeta.checked);
});
optionElements.transparency.addEventListener('change', function () {
var checked = optionElements.transparency.checked;
term.setOption('allowTransparency', checked);
term.setOption('theme', checked ? {background: 'rgba(0, 0, 0, .5)'} : {});
});
optionElements.cursorStyle.addEventListener('change', function () {
term.setOption('cursorStyle', optionElements.cursorStyle.value);
});
optionElements.bellStyle.addEventListener('change', function () {
term.setOption('bellStyle', optionElements.bellStyle.value);
});
optionElements.macOptionIsMeta.addEventListener('change', function () {
term.setOption('macOptionIsMeta', optionElements.macOptionIsMeta.checked);
});
optionElements.scrollback.addEventListener('change', function () {
term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
});
+1
View File
@@ -150,6 +150,7 @@ export class Renderer extends EventEmitter implements IRenderer {
}
public onOptionsChanged(): void {
this.colorManager.allowTransparency = this._terminal.options.allowTransparency;
this._runOperation(l => l.onOptionsChanged(this._terminal));
}
+26 -2
View File
@@ -14,6 +14,11 @@ import LRUMap from './LRUMap';
const TEXTURE_WIDTH = 1024;
const TEXTURE_HEIGHT = 1024;
const TRANSPARENT_COLOR = {
css: 'rgba(0, 0, 0, 0)',
rgba: 0,
}
interface IGlyphCacheValue {
index: number;
isEmpty: boolean;
@@ -137,7 +142,6 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
// into a shared function.
private _drawToCache(glyph: IGlyphIdentifier, index: number): IGlyphCacheValue {
this._tmpCtx.save();
// no need to clear _tmpCtx, since we're going to draw a fully opaque background
// draw the background
let backgroundColor = this._config.colors.background;
@@ -146,8 +150,25 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
} else if (glyph.bg < 256) {
backgroundColor = this._config.colors.ansi[glyph.bg];
}
let backgroundIsTransparent = false;
if ((backgroundColor.rgba & 0xFF) !== 0xFF) {
// The background color has some transparency, so we need to render it as fully transparent
// in the atlas. Otherwise we'd end up drawing the transparent background twice around the
// anti-aliased edges of the glyph, and it would look too dark.
//
// This has the side-effect of disabling RGB subpixel antialiasing, but most compositors will
// disable that anyways on a partially transparent background for similar reasons.
backgroundColor = TRANSPARENT_COLOR;
backgroundIsTransparent = true;
}
// Use a 'copy' composite operation to clear any existing glyph out of _tmpCtx, regardless of
// transparency in backgroundColor
this._tmpCtx.globalCompositeOperation = 'copy';
this._tmpCtx.fillStyle = backgroundColor.css;
this._tmpCtx.fillRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight);
this._tmpCtx.globalCompositeOperation = 'source-over';
// draw the foreground/glyph
this._tmpCtx.font =
@@ -179,7 +200,10 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
const imageData = this._tmpCtx.getImageData(
0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight,
);
const isEmpty = clearColor(imageData, backgroundColor);
let isEmpty = false;
if (!backgroundIsTransparent) {
isEmpty = clearColor(imageData, backgroundColor);
}
// copy the data from _tmpCanvas to _cacheCanvas
const [x, y] = this._toCoordinates(index);