"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Constants_1 = require("browser/renderer/atlas/Constants"); var Constants_2 = require("common/buffer/Constants"); var CharAtlasUtils_1 = require("./CharAtlasUtils"); var WebglUtils_1 = require("../WebglUtils"); var TEXTURE_WIDTH = 1024; var TEXTURE_HEIGHT = 1024; var TEXTURE_CAPACITY = Math.floor(TEXTURE_HEIGHT * 0.8); var TRANSPARENT_COLOR = { css: 'rgba(0, 0, 0, 0)', rgba: 0 }; var NULL_RASTERIZED_GLYPH = { offset: { x: 0, y: 0 }, texturePosition: { x: 0, y: 0 }, texturePositionClipSpace: { x: 0, y: 0 }, size: { x: 0, y: 0 }, sizeClipSpace: { x: 0, y: 0 } }; var TMP_CANVAS_GLYPH_PADDING = 2; var WebglCharAtlas = (function () { function WebglCharAtlas(document, _config) { this._config = _config; this._didWarmUp = false; this._cacheMap = {}; this._cacheMapCombined = {}; this._currentRowY = 0; this._currentRowX = 0; this._currentRowHeight = 0; this.hasCanvasChanged = false; this._workBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 }; this.cacheCanvas = document.createElement('canvas'); this.cacheCanvas.width = TEXTURE_WIDTH; this.cacheCanvas.height = TEXTURE_HEIGHT; this._cacheCtx = WebglUtils_1.throwIfFalsy(this.cacheCanvas.getContext('2d', { alpha: true })); this._tmpCanvas = document.createElement('canvas'); this._tmpCanvas.width = this._config.scaledCharWidth * 2 + TMP_CANVAS_GLYPH_PADDING * 2; this._tmpCanvas.height = this._config.scaledCharHeight + TMP_CANVAS_GLYPH_PADDING * 2; this._tmpCtx = WebglUtils_1.throwIfFalsy(this._tmpCanvas.getContext('2d', { alpha: this._config.allowTransparency })); document.body.appendChild(this.cacheCanvas); } WebglCharAtlas.prototype.dispose = function () { if (this.cacheCanvas.parentElement) { this.cacheCanvas.parentElement.removeChild(this.cacheCanvas); } }; WebglCharAtlas.prototype.warmUp = function () { if (!this._didWarmUp) { this._doWarmUp(); this._didWarmUp = true; } }; WebglCharAtlas.prototype._doWarmUp = function () { var _a; for (var i = 33; i < 126; i++) { var rasterizedGlyph = this._drawToCache(i, Constants_2.DEFAULT_ATTR, Constants_2.DEFAULT_COLOR, Constants_2.DEFAULT_COLOR); this._cacheMap[i] = (_a = {}, _a[Constants_2.DEFAULT_ATTR] = rasterizedGlyph, _a); } }; WebglCharAtlas.prototype.beginFrame = function () { if (this._currentRowY > TEXTURE_CAPACITY) { this._cacheCtx.clearRect(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT); this._cacheMap = {}; this._currentRowHeight = 0; this._currentRowX = 0; this._currentRowY = 0; this._doWarmUp(); return true; } return false; }; WebglCharAtlas.prototype.getRasterizedGlyphCombinedChar = function (chars, attr, bg, fg) { var rasterizedGlyphSet = this._cacheMapCombined[chars]; if (!rasterizedGlyphSet) { rasterizedGlyphSet = {}; this._cacheMapCombined[chars] = rasterizedGlyphSet; } var rasterizedGlyph = rasterizedGlyphSet[attr]; if (!rasterizedGlyph) { rasterizedGlyph = this._drawToCache(chars, attr, bg, fg); rasterizedGlyphSet[attr] = rasterizedGlyph; } return rasterizedGlyph; }; WebglCharAtlas.prototype.getRasterizedGlyph = function (code, attr, bg, fg) { var rasterizedGlyphSet = this._cacheMap[code]; if (!rasterizedGlyphSet) { rasterizedGlyphSet = {}; this._cacheMap[code] = rasterizedGlyphSet; } var rasterizedGlyph = rasterizedGlyphSet[attr]; if (!rasterizedGlyph) { rasterizedGlyph = this._drawToCache(code, attr, bg, fg); rasterizedGlyphSet[attr] = rasterizedGlyph; } return rasterizedGlyph; }; WebglCharAtlas.prototype._getColorFromAnsiIndex = function (idx) { if (idx >= this._config.colors.ansi.length) { throw new Error('No color found for idx ' + idx); } return this._config.colors.ansi[idx]; }; WebglCharAtlas.prototype._getBackgroundColor = function (bg) { if (this._config.allowTransparency) { return TRANSPARENT_COLOR; } else if (bg === Constants_1.INVERTED_DEFAULT_COLOR) { return this._config.colors.foreground; } else if (CharAtlasUtils_1.is256Color(bg)) { return this._getColorFromAnsiIndex(bg); } return this._config.colors.background; }; WebglCharAtlas.prototype._getForegroundColor = function (fg) { if (fg === Constants_1.INVERTED_DEFAULT_COLOR) { return this._config.colors.background; } else if (CharAtlasUtils_1.is256Color(fg)) { return this._getColorFromAnsiIndex(fg); } return this._config.colors.foreground; }; WebglCharAtlas.prototype._drawToCache = function (codeOrChars, attr, bg, fg) { var chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars; this.hasCanvasChanged = true; var flags = attr >> 18; var bold = !!(flags & 1); var dim = !!(flags & 32); var italic = !!(flags & 64); this._tmpCtx.save(); var backgroundColor = this._getBackgroundColor(bg); this._tmpCtx.globalCompositeOperation = 'copy'; this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); this._tmpCtx.globalCompositeOperation = 'source-over'; var fontWeight = bold ? this._config.fontWeightBold : this._config.fontWeight; var fontStyle = italic ? 'italic' : ''; this._tmpCtx.font = fontStyle + " " + fontWeight + " " + this._config.fontSize * this._config.devicePixelRatio + "px " + this._config.fontFamily; this._tmpCtx.textBaseline = 'top'; this._tmpCtx.fillStyle = this._getForegroundColor(fg).css; if (dim) { this._tmpCtx.globalAlpha = Constants_1.DIM_OPACITY; } this._tmpCtx.fillText(chars, TMP_CANVAS_GLYPH_PADDING, TMP_CANVAS_GLYPH_PADDING); this._tmpCtx.restore(); var imageData = this._tmpCtx.getImageData(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); var isEmpty = clearColor(imageData, backgroundColor); if (isEmpty) { return NULL_RASTERIZED_GLYPH; } var rasterizedGlyph = this._findGlyphBoundingBox(imageData, this._workBoundingBox); var clippedImageData = this._clipImageData(imageData, this._workBoundingBox); if (this._currentRowX + this._config.scaledCharWidth > TEXTURE_WIDTH) { this._currentRowX = 0; this._currentRowY += this._currentRowHeight; this._currentRowHeight = 0; } rasterizedGlyph.texturePosition.x = this._currentRowX; rasterizedGlyph.texturePosition.y = this._currentRowY; rasterizedGlyph.texturePositionClipSpace.x = this._currentRowX / TEXTURE_WIDTH; rasterizedGlyph.texturePositionClipSpace.y = this._currentRowY / TEXTURE_HEIGHT; this._currentRowHeight = Math.max(this._currentRowHeight, rasterizedGlyph.size.y); this._currentRowX += rasterizedGlyph.size.x; this._cacheCtx.putImageData(clippedImageData, rasterizedGlyph.texturePosition.x, rasterizedGlyph.texturePosition.y); return rasterizedGlyph; }; WebglCharAtlas.prototype._findGlyphBoundingBox = function (imageData, boundingBox) { boundingBox.top = 0; var found = false; for (var y = 0; y < this._tmpCanvas.height; y++) { for (var x = 0; x < this._tmpCanvas.width; x++) { var alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.top = y; found = true; break; } } if (found) { break; } } boundingBox.left = 0; found = false; for (var x = 0; x < this._tmpCanvas.width; x++) { for (var y = 0; y < this._tmpCanvas.height; y++) { var alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.left = x; found = true; break; } } if (found) { break; } } boundingBox.right = this._tmpCanvas.width; found = false; for (var x = this._tmpCanvas.width - 1; x >= 0; x--) { for (var y = 0; y < this._tmpCanvas.height; y++) { var alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.right = x; found = true; break; } } if (found) { break; } } boundingBox.bottom = this._tmpCanvas.height; found = false; for (var y = this._tmpCanvas.height - 1; y >= 0; y--) { for (var x = 0; x < this._tmpCanvas.width; x++) { var alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.bottom = y; found = true; break; } } if (found) { break; } } return { texturePosition: { x: 0, y: 0 }, texturePositionClipSpace: { x: 0, y: 0 }, size: { x: boundingBox.right - boundingBox.left + 1, y: boundingBox.bottom - boundingBox.top + 1 }, sizeClipSpace: { x: (boundingBox.right - boundingBox.left + 1) / TEXTURE_WIDTH, y: (boundingBox.bottom - boundingBox.top + 1) / TEXTURE_HEIGHT }, offset: { x: -boundingBox.left + TMP_CANVAS_GLYPH_PADDING, y: -boundingBox.top + TMP_CANVAS_GLYPH_PADDING } }; }; WebglCharAtlas.prototype._clipImageData = function (imageData, boundingBox) { var width = boundingBox.right - boundingBox.left + 1; var height = boundingBox.bottom - boundingBox.top + 1; var clippedData = new Uint8ClampedArray(width * height * 4); for (var y = boundingBox.top; y <= boundingBox.bottom; y++) { for (var x = boundingBox.left; x <= boundingBox.right; x++) { var oldOffset = y * this._tmpCanvas.width * 4 + x * 4; var newOffset = (y - boundingBox.top) * width * 4 + (x - boundingBox.left) * 4; clippedData[newOffset] = imageData.data[oldOffset]; clippedData[newOffset + 1] = imageData.data[oldOffset + 1]; clippedData[newOffset + 2] = imageData.data[oldOffset + 2]; clippedData[newOffset + 3] = imageData.data[oldOffset + 3]; } } return new ImageData(clippedData, width, height); }; return WebglCharAtlas; }()); exports.WebglCharAtlas = WebglCharAtlas; function clearColor(imageData, color) { var isEmpty = true; var r = color.rgba >>> 24; var g = color.rgba >>> 16 & 0xFF; var b = color.rgba >>> 8 & 0xFF; for (var offset = 0; offset < imageData.data.length; offset += 4) { if (imageData.data[offset] === r && imageData.data[offset + 1] === g && imageData.data[offset + 2] === b) { imageData.data[offset + 3] = 0; } else { isEmpty = false; } } return isEmpty; } //# sourceMappingURL=WebglCharAtlas.js.map