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:
@@ -48,6 +48,6 @@ export default abstract class BaseCharAtlas {
|
||||
ctx: CanvasRenderingContext2D,
|
||||
glyph: IGlyphIdentifier,
|
||||
x: number,
|
||||
y: number,
|
||||
y: number
|
||||
): boolean;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -13,4 +13,5 @@ export interface IGlyphIdentifier {
|
||||
fg: number;
|
||||
bold: boolean;
|
||||
dim: boolean;
|
||||
italic: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user