<commit_before>/**
 * Copyright (c) 2017 The xterm.js authors. All rights reserved.
 * @license MIT
 */

import { DIM_OPACITY, IGlyphIdentifier, INVERTED_DEFAULT_COLOR } from './Types';
import { ICharAtlasConfig } from '../../shared/atlas/Types';
import BaseCharAtlas from './BaseCharAtlas';
import { clearColor } from '../../shared/atlas/CharAtlasGenerator';
import LRUMap from './LRUMap';

// 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.
const TEXTURE_WIDTH = 1024;
const TEXTURE_HEIGHT = 1024;

const TRANSPARENT_COLOR = {
  css: 'rgba(0, 0, 0, 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
// frame ASAP.
//
// This helps to limit the amount of damage a program can do when it would otherwise thrash the
// cache.
const DRAW_TO_CACHE_LIMIT = 100;

interface IGlyphCacheValue {
  index: number;
  isEmpty: boolean;
}

function getGlyphCacheKey(glyph: IGlyphIdentifier): string {
  return `${glyph.bg}_${glyph.fg}_${glyph.bold ? 0 : 1}${glyph.dim ? 0 : 1}${glyph.char}`;
}

export default class DynamicCharAtlas extends BaseCharAtlas {
  // An ordered map that we're using to keep track of where each glyph is in the atlas texture.
  // It's ordered so that we can determine when to remove the old entries.
  private _cacheMap: LRUMap<IGlyphCacheValue>;

  // The texture that the atlas is drawn to
  private _cacheCanvas: HTMLCanvasElement;
  private _cacheCtx: CanvasRenderingContext2D;

  // A temporary context that glyphs are drawn to before being transfered to the atlas.
  private _tmpCtx: CanvasRenderingContext2D;

  // The number of characters stored in the atlas by width/height
  private _width: number;
  private _height: number;

  private _drawToCacheCount: number = 0;

  constructor(document: Document, private _config: ICharAtlasConfig) {
    super();
    this._cacheCanvas = document.createElement('canvas');
    this._cacheCanvas.width = TEXTURE_WIDTH;
    this._cacheCanvas.height = TEXTURE_HEIGHT;
    // The canvas needs alpha because we use clearColor to convert the background color to alpha.
    // It might also contain some characters with transparent backgrounds if allowTransparency is
    // set.
    this._cacheCtx = this._cacheCanvas.getContext('2d', {alpha: true});

    const tmpCanvas = document.createElement('canvas');
    tmpCanvas.width = this._config.scaledCharWidth;
    tmpCanvas.height = this._config.scaledCharHeight;
    this._tmpCtx = tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency});

    this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth);
    this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight);
    const capacity = this._width * this._height;
    this._cacheMap = new LRUMap(capacity);
    this._cacheMap.prealloc(capacity);

    // This is useful for debugging
    // document.body.appendChild(this._cacheCanvas);
  }

  public beginFrame(): void {
    this._drawToCacheCount = 0;
  }

  public draw(
    ctx: CanvasRenderingContext2D,
    glyph: IGlyphIdentifier,
    x: number,
    y: number,
  ): boolean {
    const glyphKey = getGlyphCacheKey(glyph);
    const cacheValue = this._cacheMap.get(glyphKey);
    if (cacheValue != null) {
      this._drawFromCache(ctx, cacheValue, x, y);
      return true;
    } else if (this._canCache(glyph) && this._drawToCacheCount < DRAW_TO_CACHE_LIMIT) {
      let index;
      if (this._cacheMap.size < this._cacheMap.capacity) {
        index = this._cacheMap.size;
      } else {
        // we're out of space, so our call to set will delete this item
        index = this._cacheMap.peek().index;
      }
      const cacheValue = this._drawToCache(glyph, index);
      this._cacheMap.set(glyphKey, cacheValue);<commit_msg>Rename DRAW_TO_CACHE_LIMIT to FRAME_CACHE_DRAW_LIMIT<commit_after>/**
 * Copyright (c) 2017 The xterm.js authors. All rights reserved.
 * @license MIT
 */

import { DIM_OPACITY, IGlyphIdentifier, INVERTED_DEFAULT_COLOR } from './Types';
import { ICharAtlasConfig } from '../../shared/atlas/Types';
import BaseCharAtlas from './BaseCharAtlas';
import { clearColor } from '../../shared/atlas/CharAtlasGenerator';
import LRUMap from './LRUMap';

// 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.
const TEXTURE_WIDTH = 1024;
const TEXTURE_HEIGHT = 1024;

const TRANSPARENT_COLOR = {
  css: 'rgba(0, 0, 0, 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
// frame ASAP.
//
// This helps to limit the amount of damage a program can do when it would otherwise thrash the
// cache.
const FRAME_CACHE_DRAW_LIMIT = 100;

interface IGlyphCacheValue {
  index: number;
  isEmpty: boolean;
}

function getGlyphCacheKey(glyph: IGlyphIdentifier): string {
  return `${glyph.bg}_${glyph.fg}_${glyph.bold ? 0 : 1}${glyph.dim ? 0 : 1}${glyph.char}`;
}

export default class DynamicCharAtlas extends BaseCharAtlas {
  // An ordered map that we're using to keep track of where each glyph is in the atlas texture.
  // It's ordered so that we can determine when to remove the old entries.
  private _cacheMap: LRUMap<IGlyphCacheValue>;

  // The texture that the atlas is drawn to
  private _cacheCanvas: HTMLCanvasElement;
  private _cacheCtx: CanvasRenderingContext2D;

  // A temporary context that glyphs are drawn to before being transfered to the atlas.
  private _tmpCtx: CanvasRenderingContext2D;

  // The number of characters stored in the atlas by width/height
  private _width: number;
  private _height: number;

  private _drawToCacheCount: number = 0;

  constructor(document: Document, private _config: ICharAtlasConfig) {
    super();
    this._cacheCanvas = document.createElement('canvas');
    this._cacheCanvas.width = TEXTURE_WIDTH;
    this._cacheCanvas.height = TEXTURE_HEIGHT;
    // The canvas needs alpha because we use clearColor to convert the background color to alpha.
    // It might also contain some characters with transparent backgrounds if allowTransparency is
    // set.
    this._cacheCtx = this._cacheCanvas.getContext('2d', {alpha: true});

    const tmpCanvas = document.createElement('canvas');
    tmpCanvas.width = this._config.scaledCharWidth;
    tmpCanvas.height = this._config.scaledCharHeight;
    this._tmpCtx = tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency});

    this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth);
    this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight);
    const capacity = this._width * this._height;
    this._cacheMap = new LRUMap(capacity);
    this._cacheMap.prealloc(capacity);

    // This is useful for debugging
    // document.body.appendChild(this._cacheCanvas);
  }

  public beginFrame(): void {
    this._drawToCacheCount = 0;
  }

  public draw(
    ctx: CanvasRenderingContext2D,
    glyph: IGlyphIdentifier,
    x: number,
    y: number,
  ): boolean {
    const glyphKey = getGlyphCacheKey(glyph);
    const cacheValue = this._cacheMap.get(glyphKey);
    if (cacheValue != null) {
      this._drawFromCache(ctx, cacheValue, x, y);
      return true;
    } else if (this._canCache(glyph) && this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) {
      let index;
      if (this._cacheMap.size < this._cacheMap.capacity) {
        index = this._cacheMap.size;
      } else {
        // we're out of space, so our call to set will delete this item
        index = this._cacheMap.peek().index;
      }
      const cacheValue = this._drawToCache(glyph, index);
      this._cacheMap.set(glyphKey, cacheValue);
{'fraction_non_alphanumeric': 0.09814883836501428, 'fraction_numerical': 0.007702820226115045, 'mean_word_length': 3.6747967479674797, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3384782', 'n_tokens_mistral': 2488, 'n_tokens_neox': 2377, 'n_words': 786}