diff --git a/src/pixi/text/Text.js b/src/pixi/text/Text.js index c74d1d8..65ebdff 100644 --- a/src/pixi/text/Text.js +++ b/src/pixi/text/Text.js @@ -180,7 +180,7 @@ //calculate text width var lineWidths = []; var maxLineWidth = 0; - var ascent = this.determineFontAscent(this.style.font, this.context); + var fontProperties = this.determineFontProperties(this.style.font); for (var i = 0; i < lines.length; i++) { var lineWidth = this.context.measureText(lines[i]).width; @@ -194,7 +194,7 @@ this.canvas.width = ( width + this.context.lineWidth ) * this.resolution; //calculate text height - var lineHeight = parseInt(this.getFontProperties(this.style.font).fontSize) + this.style.strokeThickness; + var lineHeight = fontProperties.fontSize + this.style.strokeThickness; var height = lineHeight * lines.length; if(this.style.dropShadow)height += this.style.dropShadowDistance; @@ -223,7 +223,7 @@ for (i = 0; i < lines.length; i++) { linePositionX = this.style.strokeThickness / 2; - linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + ascent; + linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; if(this.style.align === 'right') { @@ -250,7 +250,7 @@ for (i = 0; i < lines.length; i++) { linePositionX = this.style.strokeThickness / 2; - linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + ascent; + linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; if(this.style.align === 'right') { @@ -338,117 +338,106 @@ }; /** -* Returns the ascent (in pixels) of the highest letter for the given font -* - returns a cached value if it has already been calculated. This means -* that if the value for a webfont is calculated before it is loaded, it will be incorrect -* @method determinFontAscent -* @param font {String} The size and type of font (e.g. '23px Arial') -* @param context {Context2D} the 2d drawing method of the canvas -* @return {number} +* Calculates the ascent, descent and fontSize of a given fontStyle +* +* @method determineFontProperties +* @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontAscent = function (font, context) +PIXI.Text.prototype.determineFontProperties = function(fontStyle) { - var fontAscent = PIXI.Text.ascentCache[font]; + var properties = PIXI.Text.fontPropertiesCache[fontStyle]; - if (!fontAscent) + if(!properties) { - // We test all uppercase letters to try to ensure that the greatest ascent will be found - var testText = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - var testMeasureTextWidth = context.measureText(testText).width; + properties = {}; + + var canvas = PIXI.Text.fontPropertiesCanvas; + var context = PIXI.Text.fontPropertiesContext; - var fontProperties = this.getFontProperties(font); - var fontSize = parseInt(fontProperties.fontSize); - var fontFamily = fontProperties.fontFamily; - fontAscent = this.getFontAscent(testText, fontFamily, fontSize, testMeasureTextWidth); - PIXI.Text.ascentCache[font] = fontAscent; + context.font = fontStyle; + + var width = Math.ceil(context.measureText('|Mq').width); + var baseline = Math.ceil(context.measureText('M').width); + var height = 2 * baseline; + + baseline = baseline * 1.4 | 0; + + canvas.width = width; + canvas.height = height; + + context.fillStyle = '#f00'; + context.fillRect(0, 0, width, height); + + context.font = fontStyle; + + context.textBaseline = 'alphabetic'; + context.fillStyle = '#000'; + context.fillText('|Mq', 0, baseline); + + var imagedata = context.getImageData(0, 0, width, height).data; + var pixels = imagedata.length; + var line = width * 4; + + var i, j; + + var idx = 0; + var stop = false; + + // ascent. scan from top to bottom until we find a non red pixel + for(i = 0; i < baseline; i++) + { + for(j = 0; j < line; j += 4) + { + if(imagedata[idx + j] !== 255) + { + stop = true; + break; + } + } + if(!stop) + { + idx += line; + } + else + { + break; + } + } + + properties.ascent = baseline - i; + + idx = pixels - line; + stop = false; + + // descent. scan from bottom to top until we find a non red pixel + for(i = height; i > baseline; i--) + { + for(j = 0; j < line; j += 4) + { + if(imagedata[idx + j] !== 255) + { + stop = true; + break; + } + } + if(!stop) + { + idx -= line; + } + else + { + break; + } + } + + properties.descent = i - baseline; + properties.fontSize = properties.ascent + properties.descent; + + PIXI.Text.fontPropertiesCache[fontStyle] = properties; } - return fontAscent; -}; - -/** -* This code was lifted from https://github.com/Pomax/fontmetrics.js - which is covered by the MIT License - not sure how we should credit.... -* Returns the ascent (in pixels) of the highest letter for the given font -* @method getFontAscent -* @param textstring {String} - the string of text used to calculate the ascent -* @param fontFamily {String} - the Font Family of the text (e.g. 'Arial') -* @param fontSize {number} - the size (in pixels) of the text -* @param textWidth {number} - the width (in pixels) of the text -* @return {number} -* @private -*/ -PIXI.Text.prototype.getFontAscent = function (textstring, fontFamily, fontSize, textWidth) { - - var canvas = document.createElement('canvas'); - var padding = 100; - canvas.width = textWidth + padding; - canvas.height = 3 * fontSize; - canvas.style.opacity = 1; - canvas.style.fontFamily = fontFamily; - canvas.style.fontSize = fontSize; - var ctx = canvas.getContext('2d'); - ctx.font = fontSize + 'px ' + fontFamily; - - var w = canvas.width, - h = canvas.height, - baseline = h / 2; - - // Set all canvas pixeldata values to 255, with all the content - // data being 0. This lets us scan for data[i] != 255. - ctx.fillStyle = 'white'; - ctx.fillRect(-1, -1, w + 2, h + 2); - ctx.fillStyle = 'black'; - ctx.fillText(textstring, padding / 2, baseline); - var pixelData = ctx.getImageData(0, 0, w, h).data; - - // canvas pixel data is w*4 by h*4, because R, G, B and A are separate, - // consecutive values in the array, rather than stored as 32 bit ints. - var i = 1, - w4 = w * 4, - len = pixelData.length; - - // Finding the ascent uses a normal, forward scanline - while (i < len && pixelData[i] === 255) {i++; } - var ascent = (i / w4) | 0; - - return (baseline - ascent) + 1; -}; - -/** -* Returns the fontSize and fontFamily for a given font - caches the calculated values -* @method getFontProperties -* @param font {String} The size and type of font (e.g. '23px Arial') -* @return {Object} fontSize and fontFamily -* @private -**/ -PIXI.Text.prototype.getFontProperties = function (font) -{ - var fontProperties = PIXI.Text.fontPropertiesCache[font]; - - if (!fontProperties) - { - var div = document.createElement('div'); - div.style.font = font; - div.style.display = 'none'; - - var body = document.getElementsByTagName('body')[0]; - body.appendChild(div); - - var fontSize = document.defaultView.getComputedStyle(div, null).getPropertyValue('font-size'); - var fontFamily = document.defaultView.getComputedStyle(div, null).getPropertyValue('font-family'); - - fontProperties = { - fontSize: fontSize, - fontFamily: fontFamily - }; - - PIXI.Text.fontPropertiesCache[font] = fontProperties; - - body.removeChild(div); - } - - return fontProperties; + return properties; }; /** @@ -514,5 +503,6 @@ this.texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); }; -PIXI.Text.ascentCache = {}; PIXI.Text.fontPropertiesCache = {}; +PIXI.Text.fontPropertiesCanvas = document.createElement('canvas'); +PIXI.Text.fontPropertiesContext = PIXI.Text.fontPropertiesCanvas.getContext('2d');