Add CellColorResolver to share load color code
This commit is contained in:
@@ -18,6 +18,7 @@ import { IBufferService, IDecorationService, IOptionsService } from 'common/serv
|
|||||||
import { ICellData } from 'common/Types';
|
import { ICellData } from 'common/Types';
|
||||||
import { Terminal } from 'xterm';
|
import { Terminal } from 'xterm';
|
||||||
import { IRenderLayer } from './Types';
|
import { IRenderLayer } from './Types';
|
||||||
|
import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver';
|
||||||
|
|
||||||
// Work variables to avoid garbage collection
|
// Work variables to avoid garbage collection
|
||||||
let $fg = 0;
|
let $fg = 0;
|
||||||
@@ -40,6 +41,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
protected _selectionEnd: [number, number] | undefined;
|
protected _selectionEnd: [number, number] | undefined;
|
||||||
protected _columnSelectMode: boolean = false;
|
protected _columnSelectMode: boolean = false;
|
||||||
protected _selectionModel: ISelectionRenderModel = createSelectionRenderModel();
|
protected _selectionModel: ISelectionRenderModel = createSelectionRenderModel();
|
||||||
|
private _cellColorResolver: CellColorResolver;
|
||||||
|
|
||||||
protected _charAtlas!: ITextureAtlas;
|
protected _charAtlas!: ITextureAtlas;
|
||||||
|
|
||||||
@@ -58,6 +60,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
protected readonly _decorationService: IDecorationService,
|
protected readonly _decorationService: IDecorationService,
|
||||||
protected readonly _coreBrowserService: ICoreBrowserService
|
protected readonly _coreBrowserService: ICoreBrowserService
|
||||||
) {
|
) {
|
||||||
|
this._cellColorResolver = new CellColorResolver(this._terminal, this._colors, this._selectionModel, this._decorationService, this._coreBrowserService);
|
||||||
this._canvas = document.createElement('canvas');
|
this._canvas = document.createElement('canvas');
|
||||||
this._canvas.classList.add(`xterm-${id}-layer`);
|
this._canvas.classList.add(`xterm-${id}-layer`);
|
||||||
this._canvas.style.zIndex = zIndex.toString();
|
this._canvas.style.zIndex = zIndex.toString();
|
||||||
@@ -359,20 +362,18 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _workColors: { fg: number, bg: number, ext: number } = { fg: 0, bg: 0, ext: 0 };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws one or more characters at a cell. If possible this will draw using
|
* Draws one or more characters at a cell. If possible this will draw using
|
||||||
* the character atlas to reduce draw time.
|
* the character atlas to reduce draw time.
|
||||||
*/
|
*/
|
||||||
protected _drawChars(cell: ICellData, x: number, y: number): void {
|
protected _drawChars(cell: ICellData, x: number, y: number): void {
|
||||||
const chars = cell.getChars();
|
const chars = cell.getChars();
|
||||||
this._loadColorsForCell(x, y, cell, this._workColors);
|
this._cellColorResolver.resolve(cell, x, y);
|
||||||
let glyph: IRasterizedGlyph;
|
let glyph: IRasterizedGlyph;
|
||||||
if (chars && chars.length > 1) {
|
if (chars && chars.length > 1) {
|
||||||
glyph = this._charAtlas.getRasterizedGlyphCombinedChar(chars, this._workColors.bg, this._workColors.fg, this._workColors.ext);
|
glyph = this._charAtlas.getRasterizedGlyphCombinedChar(chars, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
|
||||||
} else {
|
} else {
|
||||||
glyph = this._charAtlas.getRasterizedGlyph(cell.getCode() || WHITESPACE_CELL_CODE, this._workColors.bg, this._workColors.fg, this._workColors.ext);
|
glyph = this._charAtlas.getRasterizedGlyph(cell.getCode() || WHITESPACE_CELL_CODE, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
|
||||||
}
|
}
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._clipRow(y);
|
this._clipRow(y);
|
||||||
@@ -391,103 +392,6 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
// TODO: Move both renderers to use shared load color code
|
// TODO: Move both renderers to use shared load color code
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads colors for the cell into the work colors object. This resolves overrides/inverse if
|
|
||||||
* necessary which is why the work cell object is not used.
|
|
||||||
*/
|
|
||||||
private _loadColorsForCell(x: number, y: number, cell: ICellData, workColors: { fg: number, bg: number, ext: number }): void {
|
|
||||||
workColors.bg = cell.bg;
|
|
||||||
workColors.fg = cell.fg;
|
|
||||||
workColors.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0;
|
|
||||||
// Get any foreground/background overrides, this happens on the model to avoid spreading
|
|
||||||
// override logic throughout the different sub-renderers
|
|
||||||
|
|
||||||
// Reset overrides work variables
|
|
||||||
$bg = 0;
|
|
||||||
$fg = 0;
|
|
||||||
$hasBg = false;
|
|
||||||
$hasFg = false;
|
|
||||||
$isSelected = false;
|
|
||||||
|
|
||||||
// Apply decorations on the bottom layer
|
|
||||||
this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
|
|
||||||
if (d.backgroundColorRGB) {
|
|
||||||
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasBg = true;
|
|
||||||
}
|
|
||||||
if (d.foregroundColorRGB) {
|
|
||||||
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Apply the selection color if needed
|
|
||||||
$isSelected = this._selectionModel.isCellSelected(this._terminal, x, y);
|
|
||||||
if ($isSelected) {
|
|
||||||
$bg = (this._coreBrowserService.isFocused ? this._colors.selectionBackgroundOpaque : this._colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasBg = true;
|
|
||||||
if (this._colors.selectionForeground) {
|
|
||||||
$fg = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply decorations on the top layer
|
|
||||||
this._decorationService.forEachDecorationAtCell(x, y, 'top', d => {
|
|
||||||
if (d.backgroundColorRGB) {
|
|
||||||
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasBg = true;
|
|
||||||
}
|
|
||||||
if (d.foregroundColorRGB) {
|
|
||||||
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag
|
|
||||||
// ahead of time in order to use the correct cache key
|
|
||||||
if ($hasBg) {
|
|
||||||
if ($isSelected) {
|
|
||||||
// Non-RGB attributes from model + force non-dim + override + force RGB color mode
|
|
||||||
$bg = (cell.bg & ~Attributes.RGB_MASK & ~BgFlags.DIM) | $bg | Attributes.CM_RGB;
|
|
||||||
} else {
|
|
||||||
// Non-RGB attributes from model + override + force RGB color mode
|
|
||||||
$bg = (cell.bg & ~Attributes.RGB_MASK) | $bg | Attributes.CM_RGB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($hasFg) {
|
|
||||||
// Non-RGB attributes from model + force disable inverse + override + force RGB color mode
|
|
||||||
$fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle case where inverse was specified by only one of bg override or fg override was set,
|
|
||||||
// resolving the other inverse color and setting the inverse flag if needed.
|
|
||||||
if (workColors.fg & FgFlags.INVERSE) {
|
|
||||||
if ($hasBg && !$hasFg) {
|
|
||||||
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
|
||||||
if ((workColors.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
|
||||||
$fg = (workColors.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | ((this._colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
|
||||||
} else {
|
|
||||||
$fg = (workColors.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | workColors.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
|
||||||
}
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
if (!$hasBg && $hasFg) {
|
|
||||||
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
|
||||||
if ((workColors.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
|
||||||
$bg = (workColors.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | ((this._colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
|
||||||
} else {
|
|
||||||
$bg = (workColors.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | workColors.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
|
||||||
}
|
|
||||||
$hasBg = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the override if it exists
|
|
||||||
workColors.bg = $hasBg ? $bg : workColors.bg;
|
|
||||||
workColors.fg = $hasFg ? $fg : workColors.fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clips a row to ensure no pixels will be drawn outside the cells in the row.
|
* Clips a row to ensure no pixels will be drawn outside the cells in the row.
|
||||||
* @param y The row to clip.
|
* @param y The row to clip.
|
||||||
|
|||||||
@@ -3,34 +3,28 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { GlyphRenderer } from './GlyphRenderer';
|
|
||||||
import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
|
|
||||||
import { CursorRenderLayer } from './renderLayer/CursorRenderLayer';
|
|
||||||
import { acquireTextureAtlas, removeTerminalFromCache } from 'browser/renderer/shared/CharAtlasCache';
|
|
||||||
import { RectangleRenderer } from './RectangleRenderer';
|
|
||||||
import { IWebGL2RenderingContext } from './Types';
|
|
||||||
import { RenderModel, COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
|
|
||||||
import { Disposable } from 'common/Lifecycle';
|
|
||||||
import { Attributes, BgFlags, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
|
|
||||||
import { Terminal, IEvent } from 'xterm';
|
|
||||||
import { IRenderLayer } from './renderLayer/Types';
|
|
||||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ITextureAtlas } from 'browser/renderer/shared/Types';
|
|
||||||
import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixelObserver';
|
|
||||||
import { ITerminal, IColorSet } from 'browser/Types';
|
|
||||||
import { EventEmitter, initEvent } from 'common/EventEmitter';
|
|
||||||
import { CellData } from 'common/buffer/CellData';
|
|
||||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||||
|
import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver';
|
||||||
|
import { acquireTextureAtlas, removeTerminalFromCache } from 'browser/renderer/shared/CharAtlasCache';
|
||||||
|
import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixelObserver';
|
||||||
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ITextureAtlas } from 'browser/renderer/shared/Types';
|
||||||
import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services';
|
import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services';
|
||||||
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
import { IColorSet, ITerminal } from 'browser/Types';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from 'common/buffer/AttributeData';
|
||||||
|
import { CellData } from 'common/buffer/CellData';
|
||||||
|
import { Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
|
||||||
|
import { initEvent } from 'common/EventEmitter';
|
||||||
|
import { Disposable } from 'common/Lifecycle';
|
||||||
import { ICoreService, IDecorationService } from 'common/services/Services';
|
import { ICoreService, IDecorationService } from 'common/services/Services';
|
||||||
|
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
||||||
// Work variables to avoid garbage collection
|
import { Terminal } from 'xterm';
|
||||||
let $fg = 0;
|
import { GlyphRenderer } from './GlyphRenderer';
|
||||||
let $bg = 0;
|
import { RectangleRenderer } from './RectangleRenderer';
|
||||||
let $hasFg = false;
|
import { CursorRenderLayer } from './renderLayer/CursorRenderLayer';
|
||||||
let $hasBg = false;
|
import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
|
||||||
let $isSelected = false;
|
import { IRenderLayer } from './renderLayer/Types';
|
||||||
|
import { COMBINED_CHAR_BIT_MASK, RenderModel, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
|
||||||
|
import { IWebGL2RenderingContext } from './Types';
|
||||||
|
|
||||||
export class WebglRenderer extends Disposable implements IRenderer {
|
export class WebglRenderer extends Disposable implements IRenderer {
|
||||||
private _renderLayers: IRenderLayer[];
|
private _renderLayers: IRenderLayer[];
|
||||||
@@ -39,7 +33,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
private _model: RenderModel = new RenderModel();
|
private _model: RenderModel = new RenderModel();
|
||||||
private _workCell: CellData = new CellData();
|
private _workCell: CellData = new CellData();
|
||||||
private _workColors: { fg: number, bg: number, ext: number } = { fg: 0, bg: 0, ext: 0 };
|
private _cellColorResolver: CellColorResolver;
|
||||||
|
|
||||||
private _canvas: HTMLCanvasElement;
|
private _canvas: HTMLCanvasElement;
|
||||||
private _gl: IWebGL2RenderingContext;
|
private _gl: IWebGL2RenderingContext;
|
||||||
@@ -67,6 +61,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this._cellColorResolver = new CellColorResolver(this._terminal, this._colors, this._model.selection, this._decorationService, this._coreBrowserService);
|
||||||
|
|
||||||
this._core = (this._terminal as any)._core;
|
this._core = (this._terminal as any)._core;
|
||||||
|
|
||||||
this._renderLayers = [
|
this._renderLayers = [
|
||||||
@@ -155,6 +151,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
l.reset(this._terminal);
|
l.reset(this._terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._cellColorResolver.setColors(colors);
|
||||||
this._rectangleRenderer.setColors();
|
this._rectangleRenderer.setColors();
|
||||||
|
|
||||||
this._refreshCharAtlas();
|
this._refreshCharAtlas();
|
||||||
@@ -372,11 +369,11 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
this._model.lineLengths[y] = 0;
|
this._model.lineLengths[y] = 0;
|
||||||
joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
|
joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
|
||||||
for (x = 0; x < terminal.cols; x++) {
|
for (x = 0; x < terminal.cols; x++) {
|
||||||
lastBg = this._workColors.bg;
|
lastBg = this._cellColorResolver.result.bg;
|
||||||
line.loadCell(x, cell);
|
line.loadCell(x, cell);
|
||||||
|
|
||||||
if (x === 0) {
|
if (x === 0) {
|
||||||
lastBg = this._workColors.bg;
|
lastBg = this._cellColorResolver.result.bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If true, indicates that the current character(s) to draw were joined.
|
// If true, indicates that the current character(s) to draw were joined.
|
||||||
@@ -407,7 +404,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
||||||
|
|
||||||
// Load colors/resolve overrides into work colors
|
// Load colors/resolve overrides into work colors
|
||||||
this._loadColorsForCell(x, row);
|
this._cellColorResolver.resolve(cell, x, row);
|
||||||
|
|
||||||
if (code !== NULL_CELL_CODE) {
|
if (code !== NULL_CELL_CODE) {
|
||||||
this._model.lineLengths[y] = x + 1;
|
this._model.lineLengths[y] = x + 1;
|
||||||
@@ -415,9 +412,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Nothing has changed, no updates needed
|
// Nothing has changed, no updates needed
|
||||||
if (this._model.cells[i] === code &&
|
if (this._model.cells[i] === code &&
|
||||||
this._model.cells[i + RENDER_MODEL_BG_OFFSET] === this._workColors.bg &&
|
this._model.cells[i + RENDER_MODEL_BG_OFFSET] === this._cellColorResolver.result.bg &&
|
||||||
this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._workColors.fg &&
|
this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._cellColorResolver.result.fg &&
|
||||||
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] === this._workColors.ext) {
|
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] === this._cellColorResolver.result.ext) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,11 +425,11 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Cache the results in the model
|
// Cache the results in the model
|
||||||
this._model.cells[i] = code;
|
this._model.cells[i] = code;
|
||||||
this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
|
this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
|
||||||
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;
|
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
|
||||||
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._workColors.ext;
|
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
|
||||||
|
|
||||||
this._glyphRenderer.updateCell(x, y, code, this._workColors.bg, this._workColors.fg, this._workColors.ext, chars, lastBg);
|
this._glyphRenderer.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg);
|
||||||
|
|
||||||
if (isJoined) {
|
if (isJoined) {
|
||||||
// Restore work cell
|
// Restore work cell
|
||||||
@@ -443,9 +440,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
||||||
this._glyphRenderer.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0);
|
this._glyphRenderer.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0);
|
||||||
this._model.cells[j] = NULL_CELL_CODE;
|
this._model.cells[j] = NULL_CELL_CODE;
|
||||||
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
|
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg;
|
||||||
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;
|
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
|
||||||
this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._workColors.ext;
|
this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,103 +450,6 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
this._rectangleRenderer.updateBackgrounds(this._model);
|
this._rectangleRenderer.updateBackgrounds(this._model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads colors for the cell into the work colors object. This resolves overrides/inverse if
|
|
||||||
* necessary which is why the work cell object is not used.
|
|
||||||
*/
|
|
||||||
private _loadColorsForCell(x: number, y: number): void {
|
|
||||||
this._workColors.bg = this._workCell.bg;
|
|
||||||
this._workColors.fg = this._workCell.fg;
|
|
||||||
this._workColors.ext = this._workCell.bg & BgFlags.HAS_EXTENDED ? this._workCell.extended.ext : 0;
|
|
||||||
// Get any foreground/background overrides, this happens on the model to avoid spreading
|
|
||||||
// override logic throughout the different sub-renderers
|
|
||||||
|
|
||||||
// Reset overrides work variables
|
|
||||||
$bg = 0;
|
|
||||||
$fg = 0;
|
|
||||||
$hasBg = false;
|
|
||||||
$hasFg = false;
|
|
||||||
$isSelected = false;
|
|
||||||
|
|
||||||
// Apply decorations on the bottom layer
|
|
||||||
this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
|
|
||||||
if (d.backgroundColorRGB) {
|
|
||||||
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasBg = true;
|
|
||||||
}
|
|
||||||
if (d.foregroundColorRGB) {
|
|
||||||
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Apply the selection color if needed
|
|
||||||
$isSelected = this._model.selection.isCellSelected(this._terminal, x, y);
|
|
||||||
if ($isSelected) {
|
|
||||||
$bg = (this._coreBrowserService.isFocused ? this._colors.selectionBackgroundOpaque : this._colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasBg = true;
|
|
||||||
if (this._colors.selectionForeground) {
|
|
||||||
$fg = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply decorations on the top layer
|
|
||||||
this._decorationService.forEachDecorationAtCell(x, y, 'top', d => {
|
|
||||||
if (d.backgroundColorRGB) {
|
|
||||||
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasBg = true;
|
|
||||||
}
|
|
||||||
if (d.foregroundColorRGB) {
|
|
||||||
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag
|
|
||||||
// ahead of time in order to use the correct cache key
|
|
||||||
if ($hasBg) {
|
|
||||||
if ($isSelected) {
|
|
||||||
// Non-RGB attributes from model + force non-dim + override + force RGB color mode
|
|
||||||
$bg = (this._workCell.bg & ~Attributes.RGB_MASK & ~BgFlags.DIM) | $bg | Attributes.CM_RGB;
|
|
||||||
} else {
|
|
||||||
// Non-RGB attributes from model + override + force RGB color mode
|
|
||||||
$bg = (this._workCell.bg & ~Attributes.RGB_MASK) | $bg | Attributes.CM_RGB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($hasFg) {
|
|
||||||
// Non-RGB attributes from model + force disable inverse + override + force RGB color mode
|
|
||||||
$fg = (this._workCell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle case where inverse was specified by only one of bg override or fg override was set,
|
|
||||||
// resolving the other inverse color and setting the inverse flag if needed.
|
|
||||||
if (this._workColors.fg & FgFlags.INVERSE) {
|
|
||||||
if ($hasBg && !$hasFg) {
|
|
||||||
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
|
||||||
if ((this._workColors.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
|
||||||
$fg = (this._workColors.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | ((this._colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
|
||||||
} else {
|
|
||||||
$fg = (this._workColors.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this._workColors.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
|
||||||
}
|
|
||||||
$hasFg = true;
|
|
||||||
}
|
|
||||||
if (!$hasBg && $hasFg) {
|
|
||||||
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
|
||||||
if ((this._workColors.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
|
||||||
$bg = (this._workColors.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | ((this._colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
|
||||||
} else {
|
|
||||||
$bg = (this._workColors.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this._workColors.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
|
||||||
}
|
|
||||||
$hasBg = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the override if it exists
|
|
||||||
this._workColors.bg = $hasBg ? $bg : this._workColors.bg;
|
|
||||||
this._workColors.fg = $hasFg ? $fg : this._workColors.fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculates the character and canvas dimensions.
|
* Recalculates the character and canvas dimensions.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
||||||
|
import { ICoreBrowserService } from 'browser/services/Services';
|
||||||
|
import { IColorSet } from 'browser/Types';
|
||||||
|
import { Attributes, BgFlags, FgFlags } from 'common/buffer/Constants';
|
||||||
|
import { IDecorationService } from 'common/services/Services';
|
||||||
|
import { ICellData } from 'common/Types';
|
||||||
|
import { Terminal } from 'xterm';
|
||||||
|
|
||||||
|
// Work variables to avoid garbage collection
|
||||||
|
let $fg = 0;
|
||||||
|
let $bg = 0;
|
||||||
|
let $hasFg = false;
|
||||||
|
let $hasBg = false;
|
||||||
|
let $isSelected = false;
|
||||||
|
|
||||||
|
export class CellColorResolver {
|
||||||
|
/**
|
||||||
|
* The shared result of the {@link resolve} call. This is only safe to use immediately after as
|
||||||
|
* any other calls will share object.
|
||||||
|
*/
|
||||||
|
public readonly result: { fg: number, bg: number, ext: number } = {
|
||||||
|
fg: 0,
|
||||||
|
bg: 0,
|
||||||
|
ext: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _terminal: Terminal,
|
||||||
|
private _colors: IColorSet,
|
||||||
|
private readonly _selectionRenderModel: ISelectionRenderModel,
|
||||||
|
private readonly _decorationService: IDecorationService,
|
||||||
|
private readonly _coreBrowserService: ICoreBrowserService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public setColors(colors: IColorSet): void {
|
||||||
|
this._colors = colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves colors for the cell, putting the result into the shared {@link result}. This resolves
|
||||||
|
* overrides, inverse and selection for the cell which can then be used to feed into the renderer.
|
||||||
|
*/
|
||||||
|
public resolve(cell: ICellData, x: number, y: number): void {
|
||||||
|
this.result.bg = cell.bg;
|
||||||
|
this.result.fg = cell.fg;
|
||||||
|
this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0;
|
||||||
|
// Get any foreground/background overrides, this happens on the model to avoid spreading
|
||||||
|
// override logic throughout the different sub-renderers
|
||||||
|
|
||||||
|
// Reset overrides work variables
|
||||||
|
$bg = 0;
|
||||||
|
$fg = 0;
|
||||||
|
$hasBg = false;
|
||||||
|
$hasFg = false;
|
||||||
|
$isSelected = false;
|
||||||
|
|
||||||
|
// Apply decorations on the bottom layer
|
||||||
|
this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
|
||||||
|
if (d.backgroundColorRGB) {
|
||||||
|
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
||||||
|
$hasBg = true;
|
||||||
|
}
|
||||||
|
if (d.foregroundColorRGB) {
|
||||||
|
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
||||||
|
$hasFg = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply the selection color if needed
|
||||||
|
$isSelected = this._selectionRenderModel.isCellSelected(this._terminal, x, y);
|
||||||
|
if ($isSelected) {
|
||||||
|
$bg = (this._coreBrowserService.isFocused ? this._colors.selectionBackgroundOpaque : this._colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF;
|
||||||
|
$hasBg = true;
|
||||||
|
if (this._colors.selectionForeground) {
|
||||||
|
$fg = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
|
||||||
|
$hasFg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply decorations on the top layer
|
||||||
|
this._decorationService.forEachDecorationAtCell(x, y, 'top', d => {
|
||||||
|
if (d.backgroundColorRGB) {
|
||||||
|
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
||||||
|
$hasBg = true;
|
||||||
|
}
|
||||||
|
if (d.foregroundColorRGB) {
|
||||||
|
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
|
||||||
|
$hasFg = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag
|
||||||
|
// ahead of time in order to use the correct cache key
|
||||||
|
if ($hasBg) {
|
||||||
|
if ($isSelected) {
|
||||||
|
// Non-RGB attributes from model + force non-dim + override + force RGB color mode
|
||||||
|
$bg = (cell.bg & ~Attributes.RGB_MASK & ~BgFlags.DIM) | $bg | Attributes.CM_RGB;
|
||||||
|
} else {
|
||||||
|
// Non-RGB attributes from model + override + force RGB color mode
|
||||||
|
$bg = (cell.bg & ~Attributes.RGB_MASK) | $bg | Attributes.CM_RGB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($hasFg) {
|
||||||
|
// Non-RGB attributes from model + force disable inverse + override + force RGB color mode
|
||||||
|
$fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle case where inverse was specified by only one of bg override or fg override was set,
|
||||||
|
// resolving the other inverse color and setting the inverse flag if needed.
|
||||||
|
if (this.result.fg & FgFlags.INVERSE) {
|
||||||
|
if ($hasBg && !$hasFg) {
|
||||||
|
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
||||||
|
if ((this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
||||||
|
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | ((this._colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
||||||
|
} else {
|
||||||
|
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this.result.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
||||||
|
}
|
||||||
|
$hasFg = true;
|
||||||
|
}
|
||||||
|
if (!$hasBg && $hasFg) {
|
||||||
|
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
||||||
|
if ((this.result.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
||||||
|
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | ((this._colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
||||||
|
} else {
|
||||||
|
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this.result.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
||||||
|
}
|
||||||
|
$hasBg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the override if it exists
|
||||||
|
this.result.bg = $hasBg ? $bg : this.result.bg;
|
||||||
|
this.result.fg = $hasFg ? $fg : this.result.fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user