From 5bc80fdf67d3852fa94dab3ec97e76ebfdf247cb Mon Sep 17 00:00:00 2001 From: ivanwonder Date: Mon, 11 Nov 2019 11:01:32 +0800 Subject: [PATCH 1/7] force alpha to 1 when using background color as inverted foreground color. --- src/browser/Color.test.ts | 24 ++++++++++++++++++- src/browser/Color.ts | 4 ++++ .../renderer/atlas/DynamicCharAtlas.ts | 9 ++++++- src/browser/renderer/dom/DomRenderer.ts | 4 +++- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/browser/Color.test.ts b/src/browser/Color.test.ts index 44cd52f32a..005e737687 100644 --- a/src/browser/Color.test.ts +++ b/src/browser/Color.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { blend, fromCss, toPaddedHex, toCss, toRgba } from 'browser/Color'; +import { blend, fromCss, toPaddedHex, toCss, toRgba, fromRgba } from 'browser/Color'; describe('Color', () => { describe('blend', () => { @@ -135,4 +135,26 @@ describe('Color', () => { assert.equal(toRgba(0xff, 0xff, 0xff, 0xff), 0xffffffff); }); }); + + describe('fromRgba', () => { + it('should convert an rgba number to an rgba array', () => { + assert.deepEqual(fromRgba(0x00000000), [0x00, 0x00, 0x00, 0x00]); + assert.deepEqual(fromRgba(0x10101010), [0x10, 0x10, 0x10, 0x10]); + assert.deepEqual(fromRgba(0x20202020), [0x20, 0x20, 0x20, 0x20]); + assert.deepEqual(fromRgba(0x30303030), [0x30, 0x30, 0x30, 0x30]); + assert.deepEqual(fromRgba(0x40404040), [0x40, 0x40, 0x40, 0x40]); + assert.deepEqual(fromRgba(0x50505050), [0x50, 0x50, 0x50, 0x50]); + assert.deepEqual(fromRgba(0x60606060), [0x60, 0x60, 0x60, 0x60]); + assert.deepEqual(fromRgba(0x70707070), [0x70, 0x70, 0x70, 0x70]); + assert.deepEqual(fromRgba(0x80808080), [0x80, 0x80, 0x80, 0x80]); + assert.deepEqual(fromRgba(0x90909090), [0x90, 0x90, 0x90, 0x90]); + assert.deepEqual(fromRgba(0xa0a0a0a0), [0xa0, 0xa0, 0xa0, 0xa0]); + assert.deepEqual(fromRgba(0xb0b0b0b0), [0xb0, 0xb0, 0xb0, 0xb0]); + assert.deepEqual(fromRgba(0xc0c0c0c0), [0xc0, 0xc0, 0xc0, 0xc0]); + assert.deepEqual(fromRgba(0xd0d0d0d0), [0xd0, 0xd0, 0xd0, 0xd0]); + assert.deepEqual(fromRgba(0xe0e0e0e0), [0xe0, 0xe0, 0xe0, 0xe0]); + assert.deepEqual(fromRgba(0xf0f0f0f0), [0xf0, 0xf0, 0xf0, 0xf0]); + assert.deepEqual(fromRgba(0xffffffff), [0xff, 0xff, 0xff, 0xff]); + }); + }); }); diff --git a/src/browser/Color.ts b/src/browser/Color.ts index a8ce2d16ad..a9db221552 100644 --- a/src/browser/Color.ts +++ b/src/browser/Color.ts @@ -47,3 +47,7 @@ export function toRgba(r: number, g: number, b: number, a: number = 0xFF): numbe // >>> 0 forces an unsigned int return (r << 24 | g << 16 | b << 8 | a) >>> 0; } + +export function fromRgba(value: number): [number, number, number, number] { + return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]; +} diff --git a/src/browser/renderer/atlas/DynamicCharAtlas.ts b/src/browser/renderer/atlas/DynamicCharAtlas.ts index 40103bc721..4ddaf2bde6 100644 --- a/src/browser/renderer/atlas/DynamicCharAtlas.ts +++ b/src/browser/renderer/atlas/DynamicCharAtlas.ts @@ -11,6 +11,7 @@ import { LRUMap } from 'browser/renderer/atlas/LRUMap'; import { isFirefox, isSafari } from 'common/Platform'; import { IColor } from 'browser/Types'; import { throwIfFalsy } from 'browser/renderer/RendererUtils'; +import { fromRgba, toCss } from 'browser/Color'; // In practice we're probably never going to exhaust a texture this large. For debugging purposes, // however, it can be useful to set this to a really tiny value, to verify that LRU eviction works. @@ -253,7 +254,13 @@ export class DynamicCharAtlas extends BaseCharAtlas { `${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`; this._tmpCtx.textBaseline = 'middle'; - this._tmpCtx.fillStyle = this._getForegroundColor(glyph).css; + const fgColor = this._getForegroundColor(glyph); + this._tmpCtx.fillStyle = fgColor.css; + + if (glyph.fg === INVERTED_DEFAULT_COLOR) { + const rgba = fromRgba(fgColor.rgba); + this._tmpCtx.fillStyle = toCss(rgba[0], rgba[1], rgba[2]); + } // Apply alpha to dim the character if (glyph.dim) { diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index e8fc85f660..e11a413d74 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -11,6 +11,7 @@ import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; import { IOptionsService, IBufferService } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { fromRgba, toCss } from 'browser/Color'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; const ROW_CONTAINER_CLASS = 'xterm-rows'; @@ -229,8 +230,9 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`; }); + const rgba = fromRgba(this._colors.background.rgba); styles += - `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${this._colors.background.css}; }` + + `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${toCss(rgba[0], rgba[1], rgba[2])}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`; this._themeStyleElement.innerHTML = styles; From fd868044fd4c6eb819a3dbdf429bd61b2aa59b59 Mon Sep 17 00:00:00 2001 From: ivanwonder Date: Tue, 12 Nov 2019 10:06:51 +0800 Subject: [PATCH 2/7] add a helper to make color opaque --- src/browser/Color.test.ts | 24 ++++++++++++++++++- src/browser/Color.ts | 9 +++++++ .../renderer/atlas/DynamicCharAtlas.ts | 12 +++------- src/browser/renderer/dom/DomRenderer.ts | 5 ++-- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/browser/Color.test.ts b/src/browser/Color.test.ts index 005e737687..7dea98e2e0 100644 --- a/src/browser/Color.test.ts +++ b/src/browser/Color.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { blend, fromCss, toPaddedHex, toCss, toRgba, fromRgba } from 'browser/Color'; +import { blend, fromCss, toPaddedHex, toCss, toRgba, fromRgba, opaque } from 'browser/Color'; describe('Color', () => { describe('blend', () => { @@ -157,4 +157,26 @@ describe('Color', () => { assert.deepEqual(fromRgba(0xffffffff), [0xff, 0xff, 0xff, 0xff]); }); }); + + describe('opaque', () => { + it('should make the color opaque', () => { + assert.deepEqual(opaque({ css: '#00000000', rgba: 0x00000000 }), { css: '#000000', rgba: 0x000000FF }); + assert.deepEqual(opaque({ css: '#10101010', rgba: 0x10101010 }), { css: '#101010', rgba: 0x101010FF }); + assert.deepEqual(opaque({ css: '#20202020', rgba: 0x20202020 }), { css: '#202020', rgba: 0x202020FF }); + assert.deepEqual(opaque({ css: '#30303030', rgba: 0x30303030 }), { css: '#303030', rgba: 0x303030FF }); + assert.deepEqual(opaque({ css: '#40404040', rgba: 0x40404040 }), { css: '#404040', rgba: 0x404040FF }); + assert.deepEqual(opaque({ css: '#50505050', rgba: 0x50505050 }), { css: '#505050', rgba: 0x505050FF }); + assert.deepEqual(opaque({ css: '#60606060', rgba: 0x60606060 }), { css: '#606060', rgba: 0x606060FF }); + assert.deepEqual(opaque({ css: '#70707070', rgba: 0x70707070 }), { css: '#707070', rgba: 0x707070FF }); + assert.deepEqual(opaque({ css: '#80808080', rgba: 0x80808080 }), { css: '#808080', rgba: 0x808080FF }); + assert.deepEqual(opaque({ css: '#90909090', rgba: 0x90909090 }), { css: '#909090', rgba: 0x909090FF }); + assert.deepEqual(opaque({ css: '#a0a0a0a0', rgba: 0xa0a0a0a0 }), { css: '#a0a0a0', rgba: 0xa0a0a0FF }); + assert.deepEqual(opaque({ css: '#b0b0b0b0', rgba: 0xb0b0b0b0 }), { css: '#b0b0b0', rgba: 0xb0b0b0FF }); + assert.deepEqual(opaque({ css: '#c0c0c0c0', rgba: 0xc0c0c0c0 }), { css: '#c0c0c0', rgba: 0xc0c0c0FF }); + assert.deepEqual(opaque({ css: '#d0d0d0d0', rgba: 0xd0d0d0d0 }), { css: '#d0d0d0', rgba: 0xd0d0d0FF }); + assert.deepEqual(opaque({ css: '#e0e0e0e0', rgba: 0xe0e0e0e0 }), { css: '#e0e0e0', rgba: 0xe0e0e0FF }); + assert.deepEqual(opaque({ css: '#f0f0f0f0', rgba: 0xf0f0f0f0 }), { css: '#f0f0f0', rgba: 0xf0f0f0FF }); + assert.deepEqual(opaque({ css: '#ffffffff', rgba: 0xffffffff }), { css: '#ffffff', rgba: 0xffffffFF }); + }); + }); }); diff --git a/src/browser/Color.ts b/src/browser/Color.ts index a9db221552..bc4f7d1395 100644 --- a/src/browser/Color.ts +++ b/src/browser/Color.ts @@ -51,3 +51,12 @@ export function toRgba(r: number, g: number, b: number, a: number = 0xFF): numbe export function fromRgba(value: number): [number, number, number, number] { return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]; } + +export function opaque(color: IColor): IColor { + const rgba = (color.rgba | 0xFF) >>> 0; + const [r, g, b] = fromRgba(rgba); + return { + css: toCss(r, g, b), + rgba + }; +} diff --git a/src/browser/renderer/atlas/DynamicCharAtlas.ts b/src/browser/renderer/atlas/DynamicCharAtlas.ts index 4ddaf2bde6..84a5016026 100644 --- a/src/browser/renderer/atlas/DynamicCharAtlas.ts +++ b/src/browser/renderer/atlas/DynamicCharAtlas.ts @@ -11,7 +11,7 @@ import { LRUMap } from 'browser/renderer/atlas/LRUMap'; import { isFirefox, isSafari } from 'common/Platform'; import { IColor } from 'browser/Types'; import { throwIfFalsy } from 'browser/renderer/RendererUtils'; -import { fromRgba, toCss } from 'browser/Color'; +import { opaque } from 'browser/Color'; // In practice we're probably never going to exhaust a texture this large. For debugging purposes, // however, it can be useful to set this to a really tiny value, to verify that LRU eviction works. @@ -223,7 +223,7 @@ export class DynamicCharAtlas extends BaseCharAtlas { private _getForegroundColor(glyph: IGlyphIdentifier): IColor { if (glyph.fg === INVERTED_DEFAULT_COLOR) { - return this._config.colors.background; + return opaque(this._config.colors.background); } else if (glyph.fg < 256) { // 256 color support return this._getColorFromAnsiIndex(glyph.fg); @@ -254,13 +254,7 @@ export class DynamicCharAtlas extends BaseCharAtlas { `${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`; this._tmpCtx.textBaseline = 'middle'; - const fgColor = this._getForegroundColor(glyph); - this._tmpCtx.fillStyle = fgColor.css; - - if (glyph.fg === INVERTED_DEFAULT_COLOR) { - const rgba = fromRgba(fgColor.rgba); - this._tmpCtx.fillStyle = toCss(rgba[0], rgba[1], rgba[2]); - } + this._tmpCtx.fillStyle = this._getForegroundColor(glyph).css; // Apply alpha to dim the character if (glyph.dim) { diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index e11a413d74..d776f608f7 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -11,7 +11,7 @@ import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; import { IOptionsService, IBufferService } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { fromRgba, toCss } from 'browser/Color'; +import { opaque } from 'browser/Color'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; const ROW_CONTAINER_CLASS = 'xterm-rows'; @@ -230,9 +230,8 @@ export class DomRenderer extends Disposable implements IRenderer { `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`; }); - const rgba = fromRgba(this._colors.background.rgba); styles += - `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${toCss(rgba[0], rgba[1], rgba[2])}; }` + + `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${opaque(this._colors.background).css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`; this._themeStyleElement.innerHTML = styles; From 1a5c3f936460f6e8b9705f2322d1352d29dca4d0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 11:57:52 -0800 Subject: [PATCH 3/7] Properly set non-black transparency backgrounds Before it was always set to 0 as the resolved color was pulled from an ImageData --- src/browser/Color.ts | 5 ++- src/browser/ColorManager.ts | 73 +++++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/browser/Color.ts b/src/browser/Color.ts index 2fc97f0a2b..e20f62e6e2 100644 --- a/src/browser/Color.ts +++ b/src/browser/Color.ts @@ -39,7 +39,10 @@ export function toPaddedHex(c: number): string { return s.length < 2 ? '0' + s : s; } -export function toCss(r: number, g: number, b: number): string { +export function toCss(r: number, g: number, b: number, a?: number): string { + if (a !== undefined) { + return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}${toPaddedHex(a)}`; + } return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`; } diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts index 92994cfc44..46a7710e72 100644 --- a/src/browser/ColorManager.ts +++ b/src/browser/ColorManager.ts @@ -5,7 +5,7 @@ import { IColorManager, IColor, IColorSet, IColorContrastCache } from 'browser/Types'; import { ITheme } from 'common/services/Services'; -import { fromCss, toCss, blend, toRgba } from 'browser/Color'; +import { fromCss, toCss, blend, toRgba, toPaddedHex } from 'browser/Color'; import { ColorContrastCache } from 'browser/ColorContrastCache'; const DEFAULT_FOREGROUND = fromCss('#ffffff'); @@ -159,28 +159,55 @@ export class ColorManager implements IColorManager { this._ctx.fillRect(0, 0, 1, 1); const data = this._ctx.getImageData(0, 0, 1, 1).data; - if (!allowTransparency && data[3] !== 0xFF) { - // Ideally we'd just ignore the alpha channel, but... - // - // Browsers may not give back exactly the same RGB values we put in, because most/all - // convert the color to a pre-multiplied representation. getImageData converts that back to - // a un-premultipled representation, but the precision loss may make the RGB channels unuable - // on their own. - // - // E.g. In Chrome #12345610 turns into #10305010, and in the extreme case, 0xFFFFFF00 turns - // into 0x00000000. - // - // "Note: Due to the lossy nature of converting to and from premultiplied alpha color values, - // pixels that have just been set using putImageData() might be returned to an equivalent - // getImageData() as different values." - // -- https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation - // - // So let's just use the fallback color in this case instead. - console.warn( - `Color: ${css} is using transparency, but allowTransparency is false. ` + - `Using fallback ${fallback.css}.` - ); - return fallback; + // Check if the printed color was transparent + if (data[3] !== 0xFF) { + if (!allowTransparency) { + // Ideally we'd just ignore the alpha channel, but... + // + // Browsers may not give back exactly the same RGB values we put in, because most/all + // convert the color to a pre-multiplied representation. getImageData converts that back to + // a un-premultipled representation, but the precision loss may make the RGB channels unuable + // on their own. + // + // E.g. In Chrome #12345610 turns into #10305010, and in the extreme case, 0xFFFFFF00 turns + // into 0x00000000. + // + // "Note: Due to the lossy nature of converting to and from premultiplied alpha color values, + // pixels that have just been set using putImageData() might be returned to an equivalent + // getImageData() as different values." + // -- https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation + // + // So let's just use the fallback color in this case instead. + console.warn( + `Color: ${css} is using transparency, but allowTransparency is false. ` + + `Using fallback ${fallback.css}.` + ); + return fallback; + } + let r: number; + let g: number; + let b: number; + let a: number; + let rgba: number; + if (css.length === 5) { + const num = parseInt(css.substr(1), 16); + r = ((num >> 12) & 0xF) * 16; + g = ((num >> 8) & 0xF) * 16; + b = ((num >> 4) & 0xF) * 16; + a = (num & 0xF) * 16; + rgba = (r << 24) | (g << 16) | (b << 8) | a; + } else { + rgba = parseInt(css.substr(1), 16); + r = (rgba >> 24) & 0xFF; + g = (rgba >> 16) & 0xFF; + b = (rgba >> 8) & 0xFF; + a = (rgba ) & 0xFF; + } + + return { + rgba, + css: toCss(r, g, b, a) + }; } return { From 4cd8b22a5935657b35c6d123b7cd6c597fe200a7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 11:58:26 -0800 Subject: [PATCH 4/7] Use toRgba helper --- src/browser/ColorManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/ColorManager.ts b/src/browser/ColorManager.ts index 46a7710e72..b4bcdde044 100644 --- a/src/browser/ColorManager.ts +++ b/src/browser/ColorManager.ts @@ -195,7 +195,7 @@ export class ColorManager implements IColorManager { g = ((num >> 8) & 0xF) * 16; b = ((num >> 4) & 0xF) * 16; a = (num & 0xF) * 16; - rgba = (r << 24) | (g << 16) | (b << 8) | a; + rgba = toRgba(r, g, b, a); } else { rgba = parseInt(css.substr(1), 16); r = (rgba >> 24) & 0xFF; From bceddfbc28f968d89ff84eb3ce922a01d29f8fd9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 14:22:03 -0800 Subject: [PATCH 5/7] Cover inverse non-black uncached case in canvas renderer --- src/browser/renderer/BaseRenderLayer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/renderer/BaseRenderLayer.ts b/src/browser/renderer/BaseRenderLayer.ts index 599ab114ac..f55a130ddb 100644 --- a/src/browser/renderer/BaseRenderLayer.ts +++ b/src/browser/renderer/BaseRenderLayer.ts @@ -15,7 +15,7 @@ import { IColorSet, IColor } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; import { IBufferService, IOptionsService } from 'common/services/Services'; import { throwIfFalsy } from 'browser/renderer/RendererUtils'; -import { toCss, ensureContrastRatioRgba } from 'browser/Color'; +import { toCss, ensureContrastRatioRgba, opaque } from 'browser/Color'; export abstract class BaseRenderLayer implements IRenderLayer { private _canvas: HTMLCanvasElement; @@ -325,7 +325,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { if (fgOverride) { this._ctx.fillStyle = fgOverride.css; } else if (cell.isBgDefault()) { - this._ctx.fillStyle = this._colors.background.css; + this._ctx.fillStyle = opaque(this._colors.background).css; } else if (cell.isBgRGB()) { this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`; } else { From ef05a6a05354901eef52177d03858a4e05111697 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 14:33:12 -0800 Subject: [PATCH 6/7] Draw transparent background in WebGL renderer Part of #2252 --- addons/xterm-addon-webgl/src/RectangleRenderer.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index b52a506e2c..337ceacef4 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -22,13 +22,13 @@ const enum VertexAttribLocations { const vertexShaderSource = `#version 300 es layout (location = ${VertexAttribLocations.POSITION}) in vec2 a_position; layout (location = ${VertexAttribLocations.SIZE}) in vec2 a_size; -layout (location = ${VertexAttribLocations.COLOR}) in vec3 a_color; +layout (location = ${VertexAttribLocations.COLOR}) in vec4 a_color; layout (location = ${VertexAttribLocations.UNIT_QUAD}) in vec2 a_unitquad; uniform mat4 u_projection; uniform vec2 u_resolution; -out vec3 v_color; +out vec4 v_color; void main() { vec2 zeroToOne = (a_position + (a_unitquad * a_size)) / u_resolution; @@ -39,12 +39,12 @@ void main() { const fragmentShaderSource = `#version 300 es precision lowp float; -in vec3 v_color; +in vec4 v_color; out vec4 outColor; void main() { - outColor = vec4(v_color, 1); + outColor = v_color; }`; interface IVertices { @@ -155,6 +155,7 @@ export class RectangleRenderer { private _updateCachedColors(): void { this._bgFloat = this._colorToFloat32Array(this._colors.background); + console.log('bgFloat', this._colors.background, this._bgFloat); this._selectionFloat = this._colorToFloat32Array(this._colors.selectionOpaque); } From 1ff1809ab75be9a9aeee134e6dd5c040f68efda0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 25 Nov 2019 15:04:07 -0800 Subject: [PATCH 7/7] Support opaque inverse background in webgl, add test --- .../src/RectangleRenderer.ts | 1 - .../src/WebglRenderer.api.ts | 22 +++++++++++++++---- .../src/atlas/WebglCharAtlas.ts | 7 +++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 337ceacef4..2246cddd9f 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -155,7 +155,6 @@ export class RectangleRenderer { private _updateCachedColors(): void { this._bgFloat = this._colorToFloat32Array(this._colors.background); - console.log('bgFloat', this._colors.background, this._bgFloat); this._selectionFloat = this._colorToFloat32Array(this._colors.selectionOpaque); } diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index e26e054a98..bf877dd87e 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -702,6 +702,22 @@ describe('WebGL Renderer Integration Tests', function(): void { await pollFor(page, () => getCellColor(8, 2), [64, 64, 64, 255]); }); }); + + describe('allowTransparency', async () => { + before(async () => setupBrowser({ rendererType: 'dom', allowTransparency: true})); + after(async () => browser.close()); + beforeEach(async () => page.evaluate(`window.term.reset()`)); + it('transparent background inverse', async () => { + const theme: ITheme = { + background: '#ff000080' + }; + await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`); + const data = `\\x1b[7m█\x1b[0m`; + await writeSync(data); + // Inverse background should be opaque + await pollFor(page, () => getCellColor(1, 1), [255, 0, 0, 255]); + }); + }); }); async function openTerminal(options: ITerminalOptions = {}): Promise { @@ -732,7 +748,7 @@ async function getCellColor(col: number, row: number): Promise { return await page.evaluate(`Array.from(window.result)`); } -async function setupBrowser(): Promise { +async function setupBrowser(options: ITerminalOptions = { rendererType: 'dom' }): Promise { browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, args: [`--window-size=${width},${height}`, `--no-sandbox`] @@ -740,9 +756,7 @@ async function setupBrowser(): Promise { page = (await browser.pages())[0]; await page.setViewport({ width, height }); await page.goto(APP); - await openTerminal({ - rendererType: 'dom' - }); + await openTerminal(options); await page.evaluate(` window.addon = new WebglAddon(true); window.term.loadAddon(window.addon); diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 097c420d5b..c96c2a6957 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -228,7 +228,12 @@ export class WebglCharAtlas implements IDisposable { case Attributes.CM_DEFAULT: default: if (inverse) { - return this._config.colors.background.css; + const bg = this._config.colors.background.css; + if (bg.length === 9) { + // Remove bg alpha channel if present + return bg.substr(0, 7); + } + return bg; } return this._config.colors.foreground.css; }