Skip to content

Commit

Permalink
Fix DynamicCharAtlas with transparency
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bgw committed Mar 18, 2018
1 parent 271aee4 commit 1e68f8b
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 5 deletions.
3 changes: 3 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ <h2>Options</h2>
<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
Expand Down
12 changes: 9 additions & 3 deletions demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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: '#00000080'} : {});
});
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));
});
Expand Down
22 changes: 20 additions & 2 deletions src/renderer/atlas/DynamicCharAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,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;
Expand All @@ -146,8 +145,24 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
} else if (glyph.bg < 256) {
backgroundColor = this._config.colors.ansi[glyph.bg];
}

let backgroundIsTransparent = false;
if (backgroundColor.length > 7 && backgroundColor.substr(7, 2).toLowerCase() !== 'ff') {
// 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 = '#00000000';
}

// 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;
this._tmpCtx.fillRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight);
this._tmpCtx.globalCompositeOperation = 'source-over';

// draw the foreground/glyph
this._tmpCtx.font =
Expand Down Expand Up @@ -179,7 +194,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);
Expand Down

0 comments on commit 1e68f8b

Please sign in to comment.