UNPKG

molstar

Version:

A comprehensive macromolecular library.

124 lines 6.43 kB
"use strict"; /** * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FontAtlas = exports.FontAtlasParams = exports.getFontAtlas = void 0; var tslib_1 = require("tslib"); var param_definition_1 = require("../../../mol-util/param-definition"); var distance_transform_1 = require("../../../mol-math/geometry/distance-transform"); var util_1 = require("../../../mol-gl/renderable/util"); var TextAtlasCache = {}; function getFontAtlas(props) { var hash = JSON.stringify(props); if (TextAtlasCache[hash] === undefined) { TextAtlasCache[hash] = new FontAtlas(props); } return TextAtlasCache[hash]; } exports.getFontAtlas = getFontAtlas; exports.FontAtlasParams = { fontFamily: param_definition_1.ParamDefinition.Select('sans-serif', [['sans-serif', 'Sans Serif'], ['monospace', 'Monospace'], ['serif', 'Serif'], ['cursive', 'Cursive']]), fontQuality: param_definition_1.ParamDefinition.Select(3, [[0, 'lower'], [1, 'low'], [2, 'medium'], [3, 'high'], [4, 'higher']]), fontStyle: param_definition_1.ParamDefinition.Select('normal', [['normal', 'Normal'], ['italic', 'Italic'], ['oblique', 'Oblique']]), fontVariant: param_definition_1.ParamDefinition.Select('normal', [['normal', 'Normal'], ['small-caps', 'Small Caps']]), fontWeight: param_definition_1.ParamDefinition.Select('normal', [['normal', 'Normal'], ['bold', 'Bold']]), }; var FontAtlas = /** @class */ (function () { function FontAtlas(props) { if (props === void 0) { props = {}; } this.mapped = {}; this.scratchW = 0; this.scratchH = 0; this.currentX = 0; this.currentY = 0; this.cutoff = 0.5; var p = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, param_definition_1.ParamDefinition.getDefaultValues(exports.FontAtlasParams)), props); this.props = p; // create measurements var fontSize = 32 * (p.fontQuality + 1); this.buffer = fontSize / 8; this.radius = fontSize / 3; this.lineHeight = Math.round(fontSize + 2 * this.buffer + this.radius); this.maxWidth = Math.round(this.lineHeight * 0.75); // create texture (for ~350 characters) this.texture = (0, util_1.createTextureImage)(350 * this.lineHeight * this.maxWidth, 1, Uint8Array); // prepare scratch canvas this.scratchCanvas = document.createElement('canvas'); this.scratchCanvas.width = this.maxWidth; this.scratchCanvas.height = this.lineHeight; this.scratchContext = this.scratchCanvas.getContext('2d'); this.scratchContext.font = p.fontStyle + " " + p.fontVariant + " " + p.fontWeight + " " + fontSize + "px " + p.fontFamily; this.scratchContext.fillStyle = 'black'; this.scratchContext.textBaseline = 'middle'; // SDF scratch values this.scratchData = new Uint8Array(this.lineHeight * this.maxWidth); // temporary arrays for the distance transform this.gridOuter = new Float64Array(this.lineHeight * this.maxWidth); this.gridInner = new Float64Array(this.lineHeight * this.maxWidth); this.f = new Float64Array(Math.max(this.lineHeight, this.maxWidth)); this.d = new Float64Array(Math.max(this.lineHeight, this.maxWidth)); this.z = new Float64Array(Math.max(this.lineHeight, this.maxWidth) + 1); this.v = new Int16Array(Math.max(this.lineHeight, this.maxWidth)); this.middle = Math.ceil(this.lineHeight / 2); // replacement Character this.placeholder = this.get(String.fromCharCode(0xFFFD)); } FontAtlas.prototype.get = function (char) { if (this.mapped[char] === undefined) { this.draw(char); var _a = this.texture, array = _a.array, width = _a.width, height = _a.height; var data = this.scratchData; if (this.currentX + this.scratchW > width) { this.currentX = 0; this.currentY += this.scratchH; } if (this.currentY + this.scratchH > height) { console.warn('canvas to small'); return this.placeholder; } this.mapped[char] = { x: this.currentX, y: this.currentY, w: this.scratchW, h: this.scratchH, nw: this.scratchW / this.lineHeight, nh: this.scratchH / this.lineHeight }; for (var y = 0; y < this.scratchH; ++y) { for (var x = 0; x < this.scratchW; ++x) { array[width * (this.currentY + y) + this.currentX + x] = data[y * this.scratchW + x]; } } this.currentX += this.scratchW; } return this.mapped[char]; }; FontAtlas.prototype.draw = function (char) { var h = this.lineHeight; var ctx = this.scratchContext; var data = this.scratchData; // measure text var m = ctx.measureText(char); var w = Math.min(this.maxWidth, Math.ceil(m.width + 2 * this.buffer)); var n = w * h; ctx.clearRect(0, 0, w, h); // clear scratch area ctx.fillText(char, this.buffer, this.middle); // draw text var imageData = ctx.getImageData(0, 0, w, h); for (var i = 0; i < n; i++) { var a = imageData.data[i * 4 + 3] / 255; // alpha value this.gridOuter[i] = a === 1 ? 0 : a === 0 ? Number.MAX_SAFE_INTEGER : Math.pow(Math.max(0, 0.5 - a), 2); this.gridInner[i] = a === 1 ? Number.MAX_SAFE_INTEGER : a === 0 ? 0 : Math.pow(Math.max(0, a - 0.5), 2); } (0, distance_transform_1.edt)(this.gridOuter, w, h, this.f, this.d, this.v, this.z); (0, distance_transform_1.edt)(this.gridInner, w, h, this.f, this.d, this.v, this.z); for (var i = 0; i < n; i++) { var d = this.gridOuter[i] - this.gridInner[i]; data[i] = Math.max(0, Math.min(255, Math.round(255 - 255 * (d / this.radius + this.cutoff)))); } this.scratchW = w; this.scratchH = h; }; return FontAtlas; }()); exports.FontAtlas = FontAtlas; //# sourceMappingURL=font-atlas.js.map