"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var WebglUtils_1 = require("./WebglUtils"); var WebglRenderer_1 = require("./WebglRenderer"); var RenderModel_1 = require("./RenderModel"); var TypedArrayUtils_1 = require("common/TypedArrayUtils"); var TypedArray_1 = require("./TypedArray"); var Constants_1 = require("common/buffer/Constants"); var ColorUtils_1 = require("./ColorUtils"); var vertexShaderSource = "#version 300 es\nlayout (location = " + 0 + ") in vec2 a_unitquad;\nlayout (location = " + 1 + ") in vec2 a_cellpos;\nlayout (location = " + 2 + ") in vec2 a_offset;\nlayout (location = " + 3 + ") in vec2 a_size;\nlayout (location = " + 4 + ") in vec2 a_texcoord;\nlayout (location = " + 5 + ") in vec2 a_texsize;\n\nuniform mat4 u_projection;\nuniform vec2 u_resolution;\n\nout vec2 v_texcoord;\n\nvoid main() {\n vec2 zeroToOne = (a_offset / u_resolution) + a_cellpos + (a_unitquad * a_size);\n gl_Position = u_projection * vec4(zeroToOne, 0.0, 1.0);\n v_texcoord = a_texcoord + a_unitquad * a_texsize;\n}"; var fragmentShaderSource = "#version 300 es\nprecision lowp float;\n\nin vec2 v_texcoord;\n\nuniform sampler2D u_texture;\n\nout vec4 outColor;\n\nvoid main() {\n outColor = texture(u_texture, v_texcoord);\n}"; var INDICES_PER_CELL = 10; var BYTES_PER_CELL = INDICES_PER_CELL * Float32Array.BYTES_PER_ELEMENT; var CELL_POSITION_INDICES = 2; var GlyphRenderer = (function () { function GlyphRenderer(_terminal, _colors, _gl, _dimensions) { this._terminal = _terminal; this._colors = _colors; this._gl = _gl; this._dimensions = _dimensions; this._activeBuffer = 0; this._vertices = { count: 0, attributes: new Float32Array(0), attributesBuffers: [ new Float32Array(0), new Float32Array(0) ], selectionAttributes: new Float32Array(0) }; var gl = this._gl; var program = WebglUtils_1.throwIfFalsy(WebglUtils_1.createProgram(gl, vertexShaderSource, fragmentShaderSource)); if (program === undefined) { throw new Error('Could not create WebGL program'); } this._program = program; this._projectionLocation = WebglUtils_1.throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection')); this._resolutionLocation = WebglUtils_1.throwIfFalsy(gl.getUniformLocation(this._program, 'u_resolution')); this._textureLocation = WebglUtils_1.throwIfFalsy(gl.getUniformLocation(this._program, 'u_texture')); this._vertexArrayObject = gl.createVertexArray(); gl.bindVertexArray(this._vertexArrayObject); var unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); var unitQuadVerticesBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, this._gl.FLOAT, false, 0, 0); var unitQuadElementIndices = new Uint8Array([0, 1, 3, 0, 2, 3]); var elementIndicesBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW); this._attributesBuffer = WebglUtils_1.throwIfFalsy(gl.createBuffer()); gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.enableVertexAttribArray(2); gl.vertexAttribPointer(2, 2, gl.FLOAT, false, BYTES_PER_CELL, 0); gl.vertexAttribDivisor(2, 1); gl.enableVertexAttribArray(3); gl.vertexAttribPointer(3, 2, gl.FLOAT, false, BYTES_PER_CELL, 2 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(3, 1); gl.enableVertexAttribArray(4); gl.vertexAttribPointer(4, 2, gl.FLOAT, false, BYTES_PER_CELL, 4 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(4, 1); gl.enableVertexAttribArray(5); gl.vertexAttribPointer(5, 2, gl.FLOAT, false, BYTES_PER_CELL, 6 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(5, 1); gl.enableVertexAttribArray(1); gl.vertexAttribPointer(1, 2, gl.FLOAT, false, BYTES_PER_CELL, 8 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(1, 1); this._atlasTexture = WebglUtils_1.throwIfFalsy(gl.createTexture()); gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255])); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); this.onResize(); } GlyphRenderer.prototype.beginFrame = function () { return this._atlas ? this._atlas.beginFrame() : true; }; GlyphRenderer.prototype.updateCell = function (x, y, code, attr, bg, fg, chars) { this._updateCell(this._vertices.attributes, x, y, code, attr, bg, fg, chars); }; GlyphRenderer.prototype._updateCell = function (array, x, y, code, attr, bg, fg, chars) { var terminal = this._terminal; var i = (y * terminal.cols + x) * INDICES_PER_CELL; if (code === Constants_1.NULL_CELL_CODE || code === Constants_1.WHITESPACE_CELL_CODE || code === undefined) { TypedArrayUtils_1.fill(array, 0, i, i + INDICES_PER_CELL - 1 - CELL_POSITION_INDICES); return; } var rasterizedGlyph; if (!this._atlas) { throw new Error('atlas must be set before updating cell'); } if (chars && chars.length > 1) { rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, attr, bg, fg); } else { rasterizedGlyph = this._atlas.getRasterizedGlyph(code, attr, bg, fg); } if (!rasterizedGlyph) { TypedArrayUtils_1.fill(array, 0, i, i + INDICES_PER_CELL - 1 - CELL_POSITION_INDICES); return; } array[i] = -rasterizedGlyph.offset.x + this._dimensions.scaledCharLeft; array[i + 1] = -rasterizedGlyph.offset.y + this._dimensions.scaledCharTop; array[i + 2] = rasterizedGlyph.size.x / this._dimensions.scaledCanvasWidth; array[i + 3] = rasterizedGlyph.size.y / this._dimensions.scaledCanvasHeight; array[i + 4] = rasterizedGlyph.texturePositionClipSpace.x; array[i + 5] = rasterizedGlyph.texturePositionClipSpace.y; array[i + 6] = rasterizedGlyph.sizeClipSpace.x; array[i + 7] = rasterizedGlyph.sizeClipSpace.y; }; GlyphRenderer.prototype.updateSelection = function (model, columnSelectMode) { var terminal = this._terminal; this._vertices.selectionAttributes = TypedArray_1.slice(this._vertices.attributes, 0); var lumi = ColorUtils_1.getLuminance(this._colors.background); var fg = lumi > 0.5 ? 7 : 0; var bg = lumi > 0.5 ? 0 : 7; if (columnSelectMode) { var startCol = model.selection.startCol; var width = model.selection.endCol - startCol; var height = model.selection.viewportCappedEndRow - model.selection.viewportCappedStartRow + 1; for (var y = model.selection.viewportCappedStartRow; y < model.selection.viewportCappedStartRow + height; y++) { this._updateSelectionRange(startCol, startCol + width, y, model, bg, fg); } } else { var startCol = model.selection.viewportStartRow === model.selection.viewportCappedStartRow ? model.selection.startCol : 0; var startRowEndCol = model.selection.viewportCappedStartRow === model.selection.viewportCappedEndRow ? model.selection.endCol : terminal.cols; this._updateSelectionRange(startCol, startRowEndCol, model.selection.viewportCappedStartRow, model, bg, fg); var middleRowsCount = Math.max(model.selection.viewportCappedEndRow - model.selection.viewportCappedStartRow - 1, 0); for (var y = model.selection.viewportCappedStartRow + 1; y <= model.selection.viewportCappedStartRow + middleRowsCount; y++) { this._updateSelectionRange(0, startRowEndCol, y, model, bg, fg); } if (model.selection.viewportCappedStartRow !== model.selection.viewportCappedEndRow) { var endCol = model.selection.viewportEndRow === model.selection.viewportCappedEndRow ? model.selection.endCol : terminal.cols; this._updateSelectionRange(0, endCol, model.selection.viewportCappedEndRow, model, bg, fg); } } }; GlyphRenderer.prototype._updateSelectionRange = function (startCol, endCol, y, model, bg, fg) { var terminal = this._terminal; var row = y + terminal.buffer.viewportY; var line; for (var x = startCol; x < endCol; x++) { var offset = (y * this._terminal.cols + x) * WebglRenderer_1.INDICIES_PER_CELL; var attr = model.cells[offset + 1]; attr = attr & ~0x3ffff | bg << 9 | fg; var code = model.cells[offset]; if (code & RenderModel_1.COMBINED_CHAR_BIT_MASK) { if (!line) { line = terminal.buffer.getLine(row); } var chars = line.getCell(x).char; this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], attr, bg, fg, chars); } else { this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], attr, bg, fg); } } }; GlyphRenderer.prototype.onResize = function () { var terminal = this._terminal; var gl = this._gl; gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); var newCount = terminal.cols * terminal.rows * INDICES_PER_CELL; if (this._vertices.count !== newCount) { this._vertices.count = newCount; this._vertices.attributes = new Float32Array(newCount); for (var i_1 = 0; i_1 < this._vertices.attributesBuffers.length; i_1++) { this._vertices.attributesBuffers[i_1] = new Float32Array(newCount); } var i = 0; for (var y = 0; y < terminal.rows; y++) { for (var x = 0; x < terminal.cols; x++) { this._vertices.attributes[i + 8] = x / terminal.cols; this._vertices.attributes[i + 9] = y / terminal.rows; i += INDICES_PER_CELL; } } } }; GlyphRenderer.prototype.setColors = function () { }; GlyphRenderer.prototype.render = function (renderModel, isSelectionVisible) { if (!this._atlas) { return; } var gl = this._gl; gl.useProgram(this._program); gl.bindVertexArray(this._vertexArrayObject); this._activeBuffer = (this._activeBuffer + 1) % 2; var activeBuffer = this._vertices.attributesBuffers[this._activeBuffer]; var bufferLength = 0; for (var y = 0; y < renderModel.lineLengths.length; y++) { var si = y * this._terminal.cols * INDICES_PER_CELL; var sub = (isSelectionVisible ? this._vertices.selectionAttributes : this._vertices.attributes).subarray(si, si + renderModel.lineLengths[y] * INDICES_PER_CELL); activeBuffer.set(sub, bufferLength); bufferLength += sub.length; } gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW); if (this._atlas.hasCanvasChanged) { this._atlas.hasCanvasChanged = false; gl.uniform1i(this._textureLocation, 0); gl.activeTexture(gl.TEXTURE0 + 0); gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.cacheCanvas); gl.generateMipmap(gl.TEXTURE_2D); } gl.uniformMatrix4fv(this._projectionLocation, false, WebglUtils_1.PROJECTION_MATRIX); gl.uniform2f(this._resolutionLocation, gl.canvas.width, gl.canvas.height); gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, bufferLength / INDICES_PER_CELL); }; GlyphRenderer.prototype.setAtlas = function (atlas) { var gl = this._gl; this._atlas = atlas; gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.cacheCanvas); gl.generateMipmap(gl.TEXTURE_2D); }; GlyphRenderer.prototype.setDimensions = function (dimensions) { this._dimensions = dimensions; }; return GlyphRenderer; }()); exports.GlyphRenderer = GlyphRenderer; //# sourceMappingURL=GlyphRenderer.js.map