Merge branch 'master' into dynamic-char-atlas

I did a bunch of testing of various ansi escape codes (including
italics) with all three char atlas implementations (none, static,
dynamic), with and without `drawBoldTextInBrightColors`, and with and
without `enableBold` to test all of the edge cases that I could.
This commit is contained in:
Benjamin Woodruff
2018-05-17 22:42:52 -07:00
27 changed files with 259 additions and 189 deletions
+1 -1
View File
@@ -48,6 +48,6 @@ export default abstract class BaseCharAtlas {
ctx: CanvasRenderingContext2D,
glyph: IGlyphIdentifier,
x: number,
y: number,
y: number
): boolean;
}
+11 -12
View File
@@ -15,7 +15,7 @@ import StaticCharAtlas from './StaticCharAtlas';
const charAtlasImplementations = {
'none': NoneCharAtlas,
'static': StaticCharAtlas,
'dynamic': DynamicCharAtlas,
'dynamic': DynamicCharAtlas
};
interface ICharAtlasCacheEntry {
@@ -38,7 +38,7 @@ export function acquireCharAtlas(
terminal: ITerminal,
colors: IColorSet,
scaledCharWidth: number,
scaledCharHeight: number,
scaledCharHeight: number
): BaseCharAtlas {
const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors);
@@ -51,15 +51,14 @@ export function acquireCharAtlas(
if (ownedByIndex >= 0) {
if (configEquals(entry.config, newConfig)) {
return entry.atlas;
} else {
// The configs differ, release the terminal from the entry
if (entry.ownedBy.length === 1) {
charAtlasCache.splice(i, 1);
} else {
entry.ownedBy.splice(ownedByIndex, 1);
}
break;
}
// The configs differ, release the terminal from the entry
if (entry.ownedBy.length === 1) {
charAtlasCache.splice(i, 1);
} else {
entry.ownedBy.splice(ownedByIndex, 1);
}
break;
}
}
@@ -76,10 +75,10 @@ export function acquireCharAtlas(
const newEntry: ICharAtlasCacheEntry = {
atlas: new charAtlasImplementations[terminal.options.experimentalCharAtlas](
document,
newConfig,
newConfig
),
config: newConfig,
ownedBy: [terminal],
ownedBy: [terminal]
};
charAtlasCache.push(newEntry);
return newEntry.atlas;
+1 -1
View File
@@ -17,7 +17,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
selection: null,
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
// dynamic character atlas.
ansi: colors.ansi.slice(0, 16),
ansi: colors.ansi.slice(0, 16)
};
return {
type: terminal.options.experimentalCharAtlas,
+13 -17
View File
@@ -18,8 +18,8 @@ const TEXTURE_HEIGHT = 1024;
const TRANSPARENT_COLOR = {
css: 'rgba(0, 0, 0, 0)',
rgba: 0,
}
rgba: 0
};
// Drawing to the cache is expensive: If we have to draw more than this number of glyphs to the
// cache in a single frame, give up on trying to cache anything else, and try to finish the current
@@ -35,7 +35,8 @@ interface IGlyphCacheValue {
}
function getGlyphCacheKey(glyph: IGlyphIdentifier): string {
return `${glyph.bg}_${glyph.fg}_${glyph.bold ? 0 : 1}${glyph.dim ? 0 : 1}${glyph.char}`;
const styleFlags = (glyph.bold ? 0 : 4) + (glyph.dim ? 0 : 2) + (glyph.italic ? 0 : 1);
return `${glyph.bg}_${glyph.fg}_${styleFlags}${glyph.char}`;
}
export default class DynamicCharAtlas extends BaseCharAtlas {
@@ -89,7 +90,7 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
ctx: CanvasRenderingContext2D,
glyph: IGlyphIdentifier,
x: number,
y: number,
y: number
): boolean {
const glyphKey = getGlyphCacheKey(glyph);
const cacheValue = this._cacheMap.get(glyphKey);
@@ -108,9 +109,8 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
this._cacheMap.set(glyphKey, cacheValue);
this._drawFromCache(ctx, cacheValue, x, y);
return true;
} else {
return false;
}
return false;
}
private _canCache(glyph: IGlyphIdentifier): boolean {
@@ -151,7 +151,7 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
x,
y,
this._config.scaledCharWidth,
this._config.scaledCharHeight,
this._config.scaledCharHeight
);
}
@@ -172,9 +172,8 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
return this._config.colors.foreground;
} else if (glyph.bg < 256) {
return this._getColorFromAnsiIndex(glyph.bg);
} else {
return this._config.colors.background;
}
return this._config.colors.background;
}
private _getForegroundColor(glyph: IGlyphIdentifier): IColor {
@@ -183,9 +182,8 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
} else if (glyph.fg < 256) {
// 256 color support
return this._getColorFromAnsiIndex(glyph.fg);
} else {
return this._config.colors.foreground;
}
return this._config.colors.foreground;
}
// TODO: We do this (or something similar) in multiple places. We should split this off
@@ -206,11 +204,9 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
// draw the foreground/glyph
const fontWeight = glyph.bold ? this._config.fontWeightBold : this._config.fontWeight;
const fontStyle = glyph.italic ? 'italic' : '';
this._tmpCtx.font =
`${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`;
if (glyph.bold) {
this._tmpCtx.font = `bold ${this._tmpCtx.font}`;
}
`${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`;
this._tmpCtx.textBaseline = 'top';
this._tmpCtx.fillStyle = this._getForegroundColor(glyph).css;
@@ -226,7 +222,7 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
// clear the background from the character to avoid issues with drawing over the previous
// character if it extends past it's bounds
const imageData = this._tmpCtx.getImageData(
0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight,
0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight
);
let isEmpty = false;
if (!this._config.allowTransparency) {
@@ -240,7 +236,7 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
return {
index,
isEmpty,
isEmpty
};
}
}
+7 -7
View File
@@ -4,10 +4,10 @@
*/
interface ILinkedListNode<T> {
prev: ILinkedListNode<T>,
next: ILinkedListNode<T>,
key: string,
value: T,
prev: ILinkedListNode<T>;
next: ILinkedListNode<T>;
key: string;
value: T;
}
export default class LRUMap<T> {
@@ -56,14 +56,14 @@ export default class LRUMap<T> {
* Each empty object only consumes about 60 bytes of memory, so this is pretty cheap, even for
* large maps.
*/
public prealloc(count: number) {
public prealloc(count: number): void {
const nodePool = this._nodePool;
for (let i = 0; i < count; i++) {
nodePool.push({
prev: null,
next: null,
key: null,
value: null,
value: null
});
}
}
@@ -114,7 +114,7 @@ export default class LRUMap<T> {
prev: null,
next: null,
key,
value,
value
};
}
this._map[key] = node;
+7 -17
View File
@@ -22,33 +22,31 @@ export default class StaticCharAtlas extends BaseCharAtlas {
return canvas;
}
public _doWarmUp() {
public _doWarmUp(): void {
const result = generateStaticCharAtlasTexture(window, this._canvasFactory, this._config);
if (result instanceof HTMLCanvasElement) {
this._texture = result;
} else {
result.then(texture => {
this._texture = texture
this._texture = texture;
});
}
}
private _isCached(glyph: IGlyphIdentifier, colorIndex: number): boolean {
const isAscii = glyph.code < 256;
// A color is basic if it is one of the standard normal or bold weight
// colors of the characters held in the char atlas. Note that this excludes
// the normal weight _light_ color characters.
const isBasicColor = (colorIndex > 1 && glyph.fg < 16) && (glyph.fg < 8 || glyph.bold);
// A color is basic if it is one of the 4 bit ANSI colors.
const isBasicColor = glyph.fg < 16;
const isDefaultColor = glyph.fg >= 256;
const isDefaultBackground = glyph.bg >= 256;
return isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground;
return isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground && !glyph.italic;
}
public draw(
ctx: CanvasRenderingContext2D,
glyph: IGlyphIdentifier,
x: number,
y: number,
y: number
): boolean {
// we're not warmed up yet
if (this._texture == null) {
@@ -57,7 +55,7 @@ export default class StaticCharAtlas extends BaseCharAtlas {
let colorIndex = 0;
if (glyph.fg < 256) {
colorIndex = glyph.fg + 2;
colorIndex = 2 + glyph.fg + (glyph.bold ? 16 : 0);
} else {
// If default color and bold
if (glyph.bold) {
@@ -76,14 +74,6 @@ export default class StaticCharAtlas extends BaseCharAtlas {
ctx.globalAlpha = DIM_OPACITY;
}
// Draw the non-bold version of the same color if bold is not enabled
/*if (glyph.bold && !terminal.options.enableBold) {
// Ignore default color as it's not touched above
if (colorIndex > 1) {
colorIndex -= 8;
}
}*/
ctx.drawImage(
this._texture,
glyph.code * charAtlasCellWidth,
+1
View File
@@ -13,4 +13,5 @@ export interface IGlyphIdentifier {
fg: number;
bold: boolean;
dim: boolean;
italic: boolean;
}