|
@@ -25,10 +25,11 @@ var BABYLON;
|
|
* @param samplingMode the texture sampling mode
|
|
* @param samplingMode the texture sampling mode
|
|
* @param superSample if true the FontTexture will be created with a font of a size twice bigger than the given one but all properties (lineHeight, charWidth, etc.) will be according to the original size. This is made to improve the text quality.
|
|
* @param superSample if true the FontTexture will be created with a font of a size twice bigger than the given one but all properties (lineHeight, charWidth, etc.) will be according to the original size. This is made to improve the text quality.
|
|
*/
|
|
*/
|
|
- function FontTexture(name, font, scene, maxCharCount, samplingMode, superSample) {
|
|
|
|
|
|
+ function FontTexture(name, font, scene, maxCharCount, samplingMode, superSample, signedDistanceField) {
|
|
if (maxCharCount === void 0) { maxCharCount = 200; }
|
|
if (maxCharCount === void 0) { maxCharCount = 200; }
|
|
if (samplingMode === void 0) { samplingMode = BABYLON.Texture.TRILINEAR_SAMPLINGMODE; }
|
|
if (samplingMode === void 0) { samplingMode = BABYLON.Texture.TRILINEAR_SAMPLINGMODE; }
|
|
if (superSample === void 0) { superSample = false; }
|
|
if (superSample === void 0) { superSample = false; }
|
|
|
|
+ if (signedDistanceField === void 0) { signedDistanceField = false; }
|
|
_super.call(this, null, scene, true, false, samplingMode);
|
|
_super.call(this, null, scene, true, false, samplingMode);
|
|
this._charInfos = {};
|
|
this._charInfos = {};
|
|
this._curCharCount = 0;
|
|
this._curCharCount = 0;
|
|
@@ -37,8 +38,11 @@ var BABYLON;
|
|
this.name = name;
|
|
this.name = name;
|
|
this.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
|
|
this.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
|
|
this.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
|
|
this.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
|
|
|
|
+ this._sdfScale = 8;
|
|
|
|
+ this._signedDistanceField = signedDistanceField;
|
|
this._superSample = false;
|
|
this._superSample = false;
|
|
- if (superSample) {
|
|
|
|
|
|
+ // SDF will use supersample no matter what, the resolution is otherwise too poor to produce correct result
|
|
|
|
+ if (superSample || signedDistanceField) {
|
|
var sfont = this.getSuperSampleFont(font);
|
|
var sfont = this.getSuperSampleFont(font);
|
|
if (sfont) {
|
|
if (sfont) {
|
|
this._superSample = true;
|
|
this._superSample = true;
|
|
@@ -50,22 +54,25 @@ var BABYLON;
|
|
this._context = this._canvas.getContext("2d");
|
|
this._context = this._canvas.getContext("2d");
|
|
this._context.font = font;
|
|
this._context.font = font;
|
|
this._context.fillStyle = "white";
|
|
this._context.fillStyle = "white";
|
|
|
|
+ this._context.textBaseline = "top";
|
|
this._cachedFontId = null;
|
|
this._cachedFontId = null;
|
|
var res = this.getFontHeight(font);
|
|
var res = this.getFontHeight(font);
|
|
- this._lineHeightSuper = res.height;
|
|
|
|
- this._lineHeight = this._superSample ? (this._lineHeightSuper / 2) : this._lineHeightSuper;
|
|
|
|
|
|
+ this._lineHeightSuper = res.height + 4;
|
|
|
|
+ this._lineHeight = this._superSample ? (Math.ceil(this._lineHeightSuper / 2)) : this._lineHeightSuper;
|
|
this._offset = res.offset - 1;
|
|
this._offset = res.offset - 1;
|
|
|
|
+ this._xMargin = 1 + Math.ceil(this._lineHeightSuper / 15); // Right now this empiric formula seems to work...
|
|
|
|
+ this._yMargin = this._xMargin;
|
|
var maxCharWidth = this._context.measureText("W").width;
|
|
var maxCharWidth = this._context.measureText("W").width;
|
|
this._spaceWidthSuper = this._context.measureText(" ").width;
|
|
this._spaceWidthSuper = this._context.measureText(" ").width;
|
|
this._spaceWidth = this._superSample ? (this._spaceWidthSuper / 2) : this._spaceWidthSuper;
|
|
this._spaceWidth = this._superSample ? (this._spaceWidthSuper / 2) : this._spaceWidthSuper;
|
|
// This is an approximate size, but should always be able to fit at least the maxCharCount
|
|
// This is an approximate size, but should always be able to fit at least the maxCharCount
|
|
- var totalEstSurface = this._lineHeightSuper * maxCharWidth * maxCharCount;
|
|
|
|
|
|
+ var totalEstSurface = (this._lineHeightSuper + this._yMargin) * (maxCharWidth + this._xMargin) * maxCharCount;
|
|
var edge = Math.sqrt(totalEstSurface);
|
|
var edge = Math.sqrt(totalEstSurface);
|
|
var textSize = Math.pow(2, Math.ceil(Math.log(edge) / Math.log(2)));
|
|
var textSize = Math.pow(2, Math.ceil(Math.log(edge) / Math.log(2)));
|
|
// Create the texture that will store the font characters
|
|
// Create the texture that will store the font characters
|
|
this._texture = scene.getEngine().createDynamicTexture(textSize, textSize, false, samplingMode);
|
|
this._texture = scene.getEngine().createDynamicTexture(textSize, textSize, false, samplingMode);
|
|
var textureSize = this.getSize();
|
|
var textureSize = this.getSize();
|
|
- this.hasAlpha = true;
|
|
|
|
|
|
+ this.hasAlpha = this._signedDistanceField === false;
|
|
// Recreate a new canvas with the final size: the one matching the texture (resizing the previous one doesn't work as one would expect...)
|
|
// Recreate a new canvas with the final size: the one matching the texture (resizing the previous one doesn't work as one would expect...)
|
|
this._canvas = document.createElement("canvas");
|
|
this._canvas = document.createElement("canvas");
|
|
this._canvas.width = textureSize.width;
|
|
this._canvas.width = textureSize.width;
|
|
@@ -75,6 +82,24 @@ var BABYLON;
|
|
this._context.font = font;
|
|
this._context.font = font;
|
|
this._context.fillStyle = "white";
|
|
this._context.fillStyle = "white";
|
|
this._context.imageSmoothingEnabled = false;
|
|
this._context.imageSmoothingEnabled = false;
|
|
|
|
+ this._context.clearRect(0, 0, textureSize.width, textureSize.height);
|
|
|
|
+ // Create a canvas for the signed distance field mode, we only have to store one char, the purpose is to render a char scaled _sdfScale times
|
|
|
|
+ // into this 2D context, then get the bitmap data, create the sdf char and push the result in the _context (which hold the whole Font Texture content)
|
|
|
|
+ // So you can see this context as an intermediate one, because it is.
|
|
|
|
+ if (this._signedDistanceField) {
|
|
|
|
+ var sdfC = document.createElement("canvas");
|
|
|
|
+ var s = this._sdfScale;
|
|
|
|
+ sdfC.width = maxCharWidth * s;
|
|
|
|
+ sdfC.height = this._lineHeightSuper * s;
|
|
|
|
+ var sdfCtx = sdfC.getContext("2d");
|
|
|
|
+ sdfCtx.scale(s, s);
|
|
|
|
+ sdfCtx.textBaseline = "top";
|
|
|
|
+ sdfCtx.font = font;
|
|
|
|
+ sdfCtx.fillStyle = "white";
|
|
|
|
+ sdfCtx.imageSmoothingEnabled = false;
|
|
|
|
+ this._sdfCanvas = sdfC;
|
|
|
|
+ this._sdfContext = sdfCtx;
|
|
|
|
+ }
|
|
this._currentFreePosition = BABYLON.Vector2.Zero();
|
|
this._currentFreePosition = BABYLON.Vector2.Zero();
|
|
// Add the basic ASCII based characters
|
|
// Add the basic ASCII based characters
|
|
for (var i = 0x20; i < 0x7F; i++) {
|
|
for (var i = 0x20; i < 0x7F; i++) {
|
|
@@ -90,6 +115,13 @@ var BABYLON;
|
|
enumerable: true,
|
|
enumerable: true,
|
|
configurable: true
|
|
configurable: true
|
|
});
|
|
});
|
|
|
|
+ Object.defineProperty(FontTexture.prototype, "isSignedDistanceField", {
|
|
|
|
+ get: function () {
|
|
|
|
+ return this._signedDistanceField;
|
|
|
|
+ },
|
|
|
|
+ enumerable: true,
|
|
|
|
+ configurable: true
|
|
|
|
+ });
|
|
Object.defineProperty(FontTexture.prototype, "spaceWidth", {
|
|
Object.defineProperty(FontTexture.prototype, "spaceWidth", {
|
|
get: function () {
|
|
get: function () {
|
|
return this._spaceWidth;
|
|
return this._spaceWidth;
|
|
@@ -104,32 +136,34 @@ var BABYLON;
|
|
enumerable: true,
|
|
enumerable: true,
|
|
configurable: true
|
|
configurable: true
|
|
});
|
|
});
|
|
- FontTexture.GetCachedFontTexture = function (scene, fontName, supersample) {
|
|
|
|
|
|
+ FontTexture.GetCachedFontTexture = function (scene, fontName, supersample, signedDistanceField) {
|
|
if (supersample === void 0) { supersample = false; }
|
|
if (supersample === void 0) { supersample = false; }
|
|
|
|
+ if (signedDistanceField === void 0) { signedDistanceField = false; }
|
|
var s = scene;
|
|
var s = scene;
|
|
if (!s.__fontTextureCache__) {
|
|
if (!s.__fontTextureCache__) {
|
|
s.__fontTextureCache__ = new BABYLON.StringDictionary();
|
|
s.__fontTextureCache__ = new BABYLON.StringDictionary();
|
|
}
|
|
}
|
|
var dic = s.__fontTextureCache__;
|
|
var dic = s.__fontTextureCache__;
|
|
- var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS");
|
|
|
|
|
|
+ var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS") + (signedDistanceField ? "_+SDF" : "_-SDF");
|
|
var ft = dic.get(lfn);
|
|
var ft = dic.get(lfn);
|
|
if (ft) {
|
|
if (ft) {
|
|
++ft._usedCounter;
|
|
++ft._usedCounter;
|
|
return ft;
|
|
return ft;
|
|
}
|
|
}
|
|
- ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, BABYLON.Texture.BILINEAR_SAMPLINGMODE, supersample);
|
|
|
|
|
|
+ ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, BABYLON.Texture.BILINEAR_SAMPLINGMODE, supersample, signedDistanceField);
|
|
ft._cachedFontId = lfn;
|
|
ft._cachedFontId = lfn;
|
|
dic.add(lfn, ft);
|
|
dic.add(lfn, ft);
|
|
return ft;
|
|
return ft;
|
|
};
|
|
};
|
|
- FontTexture.ReleaseCachedFontTexture = function (scene, fontName, supersample) {
|
|
|
|
|
|
+ FontTexture.ReleaseCachedFontTexture = function (scene, fontName, supersample, signedDistanceField) {
|
|
if (supersample === void 0) { supersample = false; }
|
|
if (supersample === void 0) { supersample = false; }
|
|
|
|
+ if (signedDistanceField === void 0) { signedDistanceField = false; }
|
|
var s = scene;
|
|
var s = scene;
|
|
var dic = s.__fontTextureCache__;
|
|
var dic = s.__fontTextureCache__;
|
|
if (!dic) {
|
|
if (!dic) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS");
|
|
|
|
|
|
+ var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS") + (signedDistanceField ? "_+SDF" : "_-SDF");
|
|
var font = dic.get(lfn);
|
|
var font = dic.get(lfn);
|
|
if (--font._usedCounter === 0) {
|
|
if (--font._usedCounter === 0) {
|
|
dic.remove(lfn);
|
|
dic.remove(lfn);
|
|
@@ -154,29 +188,164 @@ var BABYLON;
|
|
var textureSize = this.getSize();
|
|
var textureSize = this.getSize();
|
|
// we reached the end of the current line?
|
|
// we reached the end of the current line?
|
|
var width = Math.round(measure.width);
|
|
var width = Math.round(measure.width);
|
|
- var xMargin = 1 + Math.ceil(this._lineHeightSuper / 15); // Right now this empiric formula seems to work...
|
|
|
|
- var yMargin = xMargin;
|
|
|
|
- if (this._currentFreePosition.x + width + xMargin > textureSize.width) {
|
|
|
|
|
|
+ if (this._currentFreePosition.x + width + this._xMargin > textureSize.width) {
|
|
this._currentFreePosition.x = 0;
|
|
this._currentFreePosition.x = 0;
|
|
- this._currentFreePosition.y += this._lineHeightSuper + yMargin;
|
|
|
|
|
|
+ this._currentFreePosition.y += this._lineHeightSuper + this._yMargin;
|
|
// No more room?
|
|
// No more room?
|
|
if (this._currentFreePosition.y > textureSize.height) {
|
|
if (this._currentFreePosition.y > textureSize.height) {
|
|
return this.getChar("!");
|
|
return this.getChar("!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- // Draw the character in the texture
|
|
|
|
- this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
|
|
|
|
|
|
+ // In sdf mode we render the character in an intermediate 2D context which scale the character this._sdfScale times (which is required to compute the sdf map accurately)
|
|
|
|
+ if (this._signedDistanceField) {
|
|
|
|
+ this._sdfContext.clearRect(0, 0, this._sdfCanvas.width, this._sdfCanvas.height);
|
|
|
|
+ this._sdfContext.fillText(char, 0, -this._offset);
|
|
|
|
+ var data = this._sdfContext.getImageData(0, 0, width * this._sdfScale, this._sdfCanvas.height);
|
|
|
|
+ var res = this._computeSDFChar(data);
|
|
|
|
+ this._context.putImageData(res, this._currentFreePosition.x, this._currentFreePosition.y);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ // Draw the character in the HTML canvas
|
|
|
|
+ this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
|
|
|
|
+ }
|
|
// Fill the CharInfo object
|
|
// Fill the CharInfo object
|
|
info.topLeftUV = new BABYLON.Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
|
|
info.topLeftUV = new BABYLON.Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
|
|
info.bottomRightUV = new BABYLON.Vector2((this._currentFreePosition.x + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + 2) / textureSize.height));
|
|
info.bottomRightUV = new BABYLON.Vector2((this._currentFreePosition.x + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + 2) / textureSize.height));
|
|
|
|
+ if (this._signedDistanceField) {
|
|
|
|
+ var off = 1 / textureSize.width;
|
|
|
|
+ info.topLeftUV.addInPlace(new BABYLON.Vector2(off, off));
|
|
|
|
+ info.bottomRightUV.addInPlace(new BABYLON.Vector2(off, off));
|
|
|
|
+ }
|
|
info.charWidth = this._superSample ? (width / 2) : width;
|
|
info.charWidth = this._superSample ? (width / 2) : width;
|
|
// Add the info structure
|
|
// Add the info structure
|
|
this._charInfos[char] = info;
|
|
this._charInfos[char] = info;
|
|
this._curCharCount++;
|
|
this._curCharCount++;
|
|
// Set the next position
|
|
// Set the next position
|
|
- this._currentFreePosition.x += width + xMargin;
|
|
|
|
|
|
+ this._currentFreePosition.x += width + this._xMargin;
|
|
return info;
|
|
return info;
|
|
};
|
|
};
|
|
|
|
+ FontTexture.prototype._computeSDFChar = function (source) {
|
|
|
|
+ var scl = this._sdfScale;
|
|
|
|
+ var sw = source.width;
|
|
|
|
+ var sh = source.height;
|
|
|
|
+ var dw = sw / scl;
|
|
|
|
+ var dh = sh / scl;
|
|
|
|
+ var roffx = 0;
|
|
|
|
+ var roffy = 0;
|
|
|
|
+ // We shouldn't look beyond half of the biggest between width and height
|
|
|
|
+ var radius = scl;
|
|
|
|
+ var br = radius - 1;
|
|
|
|
+ var lookupSrc = function (dx, dy, offX, offY, lookVis) {
|
|
|
|
+ var sx = dx * scl;
|
|
|
|
+ var sy = dy * scl;
|
|
|
|
+ // Looking out of the area? return true to make the test going on
|
|
|
|
+ if (((sx + offX) < 0) || ((sx + offX) >= sw) || ((sy + offY) < 0) || ((sy + offY) >= sh)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ // Get the pixel we want
|
|
|
|
+ var val = source.data[(((sy + offY) * sw) + (sx + offX)) * 4];
|
|
|
|
+ var res = (val > 0) === lookVis;
|
|
|
|
+ if (!res) {
|
|
|
|
+ roffx = offX;
|
|
|
|
+ roffy = offY;
|
|
|
|
+ }
|
|
|
|
+ return res;
|
|
|
|
+ };
|
|
|
|
+ var lookupArea = function (dx, dy, lookVis) {
|
|
|
|
+ // Fast rejection test, if we have the same result in N, S, W, E at a distance which is the radius-1 then it means the data will be consistent in this area. That's because we've scale the rendering of the letter "radius" times, so a letter's pixel will be at least radius wide
|
|
|
|
+ if (lookupSrc(dx, dy, 0, br, lookVis) &&
|
|
|
|
+ lookupSrc(dx, dy, 0, -br, lookVis) &&
|
|
|
|
+ lookupSrc(dx, dy, -br, 0, lookVis) &&
|
|
|
|
+ lookupSrc(dx, dy, br, 0, lookVis)) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ for (var i = 1; i <= radius; i++) {
|
|
|
|
+ // Quick test N, S, W, E
|
|
|
|
+ if (!lookupSrc(dx, dy, 0, i, lookVis) || !lookupSrc(dx, dy, 0, -i, lookVis) || !lookupSrc(dx, dy, -i, 0, lookVis) || !lookupSrc(dx, dy, i, 0, lookVis)) {
|
|
|
|
+ return i * i; // Squared Distance is simple to compute in this case
|
|
|
|
+ }
|
|
|
|
+ // Test the frame area (except the N, S, W, E spots) from the nearest point from the center to the further one
|
|
|
|
+ for (var j = 1; j <= i; j++) {
|
|
|
|
+ if (!lookupSrc(dx, dy, -j, i, lookVis) || !lookupSrc(dx, dy, j, i, lookVis) ||
|
|
|
|
+ !lookupSrc(dx, dy, i, -j, lookVis) || !lookupSrc(dx, dy, i, j, lookVis) ||
|
|
|
|
+ !lookupSrc(dx, dy, -j, -i, lookVis) || !lookupSrc(dx, dy, j, -i, lookVis) ||
|
|
|
|
+ !lookupSrc(dx, dy, -i, -j, lookVis) || !lookupSrc(dx, dy, -i, j, lookVis)) {
|
|
|
|
+ // We found the nearest texel having and opposite state, store the squared length
|
|
|
|
+ var res_1 = (i * i) + (j * j);
|
|
|
|
+ var count = 1;
|
|
|
|
+ // To improve quality we will sample the texels around this one, so it's 8 samples, we consider only the one having an opposite state, add them to the current res and will will compute the average at the end
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx - 1, roffy, lookVis)) {
|
|
|
|
+ res_1 += (roffx - 1) * (roffx - 1) + roffy * roffy;
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx + 1, roffy, lookVis)) {
|
|
|
|
+ res_1 += (roffx + 1) * (roffx + 1) + roffy * roffy;
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx, roffy - 1, lookVis)) {
|
|
|
|
+ res_1 += roffx * roffx + (roffy - 1) * (roffy - 1);
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx, roffy + 1, lookVis)) {
|
|
|
|
+ res_1 += roffx * roffx + (roffy + 1) * (roffy + 1);
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx - 1, roffy - 1, lookVis)) {
|
|
|
|
+ res_1 += (roffx - 1) * (roffx - 1) + (roffy - 1) * (roffy - 1);
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx + 1, roffy + 1, lookVis)) {
|
|
|
|
+ res_1 += (roffx + 1) * (roffx + 1) + (roffy + 1) * (roffy + 1);
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx + 1, roffy - 1, lookVis)) {
|
|
|
|
+ res_1 += (roffx + 1) * (roffx + 1) + (roffy - 1) * (roffy - 1);
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ if (!lookupSrc(dx, dy, roffx - 1, roffy + 1, lookVis)) {
|
|
|
|
+ res_1 += (roffx - 1) * (roffx - 1) + (roffy + 1) * (roffy + 1);
|
|
|
|
+ ++count;
|
|
|
|
+ }
|
|
|
|
+ // Compute the average based on the accumulated distance
|
|
|
|
+ return res_1 / count;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+ };
|
|
|
|
+ var tmp = new Array(dw * dh);
|
|
|
|
+ for (var y = 0; y < dh; y++) {
|
|
|
|
+ for (var x = 0; x < dw; x++) {
|
|
|
|
+ var curState = lookupSrc(x, y, 0, 0, true);
|
|
|
|
+ var d = lookupArea(x, y, curState);
|
|
|
|
+ if (d === 0) {
|
|
|
|
+ d = radius * radius * 2;
|
|
|
|
+ }
|
|
|
|
+ tmp[(y * dw) + x] = curState ? d : -d;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ var res = this._context.createImageData(dw, dh);
|
|
|
|
+ var size = dw * dh;
|
|
|
|
+ for (var j = 0; j < size; j++) {
|
|
|
|
+ var d = tmp[j];
|
|
|
|
+ var sign = (d < 0) ? -1 : 1;
|
|
|
|
+ d = Math.sqrt(Math.abs(d)) * sign;
|
|
|
|
+ d *= 127.5 / radius;
|
|
|
|
+ d += 127.5;
|
|
|
|
+ if (d < 0) {
|
|
|
|
+ d = 0;
|
|
|
|
+ }
|
|
|
|
+ else if (d > 255) {
|
|
|
|
+ d = 255;
|
|
|
|
+ }
|
|
|
|
+ d += 0.5;
|
|
|
|
+ res.data[j * 4 + 0] = d;
|
|
|
|
+ res.data[j * 4 + 1] = d;
|
|
|
|
+ res.data[j * 4 + 2] = d;
|
|
|
|
+ res.data[j * 4 + 3] = 255;
|
|
|
|
+ }
|
|
|
|
+ return res;
|
|
|
|
+ };
|
|
FontTexture.prototype.measureText = function (text, tabulationSize) {
|
|
FontTexture.prototype.measureText = function (text, tabulationSize) {
|
|
if (tabulationSize === void 0) { tabulationSize = 4; }
|
|
if (tabulationSize === void 0) { tabulationSize = 4; }
|
|
var maxWidth = 0;
|
|
var maxWidth = 0;
|