Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

optimized bitmap font rendering (closes #63)

  • Loading branch information...
commit 9bec9126a934706f1c0a94bea630e9f7ea155aa8 1 parent 65a5d3e
@PrimaryFeather PrimaryFeather authored
View
21 starling/src/starling/core/QuadBatch.as
@@ -249,22 +249,15 @@ package starling.core
}
// display object methods
-
- /** Returns an empty rectangle at the particle system's position. This class is all about
- * speed, and calculating the actual bounds would be rather expensive. */
- public override function getBounds(targetSpace:DisplayObject,
- resultRect:Rectangle=null):Rectangle
+ /** @inheritDoc */
+ public override function getBounds(targetSpace:DisplayObject, resultRect:Rectangle=null):Rectangle
{
if (resultRect == null) resultRect = new Rectangle();
- getTransformationMatrix(targetSpace, sHelperMatrix);
- transformCoords(sHelperMatrix, 0, 0, sHelperPoint);
-
- resultRect.x = sHelperPoint.x;
- resultRect.y = sHelperPoint.y;
- resultRect.width = resultRect.height = 0;
+ var transformationMatrix:Matrix = targetSpace == this ?
+ null : getTransformationMatrix(targetSpace, sHelperMatrix);
- return resultRect;
+ return mVertexData.getBounds(transformationMatrix, 0, mNumQuads*4, resultRect);
}
/** @inheritDoc */
@@ -362,6 +355,10 @@ package starling.core
return quadBatchID;
}
+ // properties
+
+ public function get numQuads():int { return mNumQuads; }
+
// program management
private static function registerPrograms():void
View
2  starling/src/starling/display/Quad.as
@@ -89,7 +89,7 @@ package starling.display
else
{
getTransformationMatrix(targetSpace, sHelperMatrix);
- mVertexData.getBounds(sHelperMatrix, resultRect);
+ mVertexData.getBounds(sHelperMatrix, 0, 4, resultRect);
}
return resultRect;
View
12 starling/src/starling/text/BitmapChar.as
@@ -65,16 +65,22 @@ package starling.text
/** The unicode ID of the char. */
public function get charID():int { return mCharID; }
- /** The number of pixels to move the char in x direction on character arrangement. */
+ /** The number of points to move the char in x direction on character arrangement. */
public function get xOffset():Number { return mXOffset; }
- /** The number of pixels to move the char in y direction on character arrangement. */
+ /** The number of points to move the char in y direction on character arrangement. */
public function get yOffset():Number { return mYOffset; }
- /** The number of pixels the cursor has to be moved to the right for the next char. */
+ /** The number of points the cursor has to be moved to the right for the next char. */
public function get xAdvance():Number { return mXAdvance; }
/** The texture of the character. */
public function get texture():Texture { return mTexture; }
+
+ /** The width of the character in points. */
+ public function get width():Number { return mTexture.width; }
+
+ /** The height of the character in points. */
+ public function get height():Number { return mTexture.height; }
}
}
View
187 starling/src/starling/text/BitmapFont.as
@@ -13,9 +13,8 @@ package starling.text
import flash.geom.Rectangle;
import flash.utils.Dictionary;
- import starling.display.DisplayObject;
+ import starling.core.QuadBatch;
import starling.display.Image;
- import starling.display.Sprite;
import starling.textures.Texture;
import starling.utils.HAlign;
import starling.utils.VAlign;
@@ -66,6 +65,9 @@ package starling.text
private var mSize:Number;
private var mLineHeight:Number;
+ /** Helper object. */
+ private static var sHelperImage:Image;
+
/** Creates a bitmap font by parsing an XML file and uses the specified texture. */
public function BitmapFont(texture:Texture, fontXml:XML=null)
{
@@ -74,8 +76,10 @@ package starling.text
mTexture = texture;
mChars = new Dictionary();
- if (fontXml)
- parseFontXml(fontXml);
+ if (fontXml) parseFontXml(fontXml);
+
+ // the actual texture does not matter, we just need to have an image at hand.
+ if (sHelperImage == null) sHelperImage = new Image(texture);
}
/** Disposes the texture of the bitmap font! */
@@ -139,49 +143,79 @@ package starling.text
mChars[charID] = bitmapChar;
}
- /** Creates a display object that contains the given text by arranging individual chars. */
- public function createDisplayObject(width:Number, height:Number, text:String,
- fontSize:Number=-1, color:uint=0xffffff,
- hAlign:String="center", vAlign:String="center",
- autoScale:Boolean=true,
- kerning:Boolean=true):DisplayObject
+ /** Draws text into a QuadBatch. */
+ public function fillQuadBatch(quadBatch:QuadBatch, width:Number, height:Number, text:String,
+ fontSize:Number=-1, color:uint=0xffffff,
+ hAlign:String="center", vAlign:String="center",
+ autoScale:Boolean=true,
+ kerning:Boolean=true):void
+ {
+ var charLocations:Vector.<CharLocation> = arrangeChars(width, height, text, fontSize,
+ hAlign, vAlign, autoScale, kerning);
+ var numChars:int = charLocations.length;
+
+ for (var i:int=0; i<numChars; ++i)
+ {
+ var charLocation:CharLocation = charLocations[i];
+ var char:BitmapChar = charLocation.char;
+ sHelperImage.texture = char.texture;
+ sHelperImage.readjustSize();
+ sHelperImage.x = charLocation.x;
+ sHelperImage.y = charLocation.y;
+ sHelperImage.scaleX = sHelperImage.scaleY = charLocation.scale;;
+ sHelperImage.color = color;
+ quadBatch.addImage(sHelperImage);
+ }
+ }
+
+ /** Arranges the characters of a text inside a rectangle, adhering to the given settings.
+ * Returns a Vector of CharLocations. */
+ private function arrangeChars(width:Number, height:Number, text:String, fontSize:Number=-1,
+ hAlign:String="center", vAlign:String="center",
+ autoScale:Boolean=true, kerning:Boolean=true):Vector.<CharLocation>
{
if (fontSize == NATIVE_SIZE) fontSize = mSize;
- var lineContainer:Sprite;
+ var lines:Vector.<Vector.<CharLocation>>;
var finished:Boolean = false;
+ var char:BitmapChar;
+ var charLocation:CharLocation;
+ var numChars:int;
+ var containerWidth:Number;
+ var containerHeight:Number;
+ var scale:Number;
while (!finished)
{
- var scale:Number = fontSize / mSize;
- lineContainer = new Sprite();
+ scale = fontSize / mSize;
+ containerWidth = width / scale;
+ containerHeight = height / scale;
- if (mLineHeight * scale <= height)
- {
- var containerWidth:Number = width / scale;
- var containerHeight:Number = height / scale;
- lineContainer.scaleX = lineContainer.scaleY = scale;
+ lines = new Vector.<Vector.<CharLocation>>();
+ if (mLineHeight <= containerHeight)
+ {
var lastWhiteSpace:int = -1;
var lastCharID:int = -1;
var currentX:Number = 0;
- var currentLine:Sprite = new Sprite();
- var numChars:int = text.length;
-
+ var currentY:Number = 0;
+ var currentLine:Vector.<CharLocation> = new <CharLocation>[];
+
+ numChars = text.length;
for (var i:int=0; i<numChars; ++i)
{
var lineFull:Boolean = false;
-
var charID:int = text.charCodeAt(i);
+
if (charID == CHAR_NEWLINE)
{
lineFull = true;
}
else
{
- var bitmapChar:BitmapChar = getChar(charID);
+ char = getChar(charID);
- if (bitmapChar == null)
+ if (char == null)
{
trace("[Starling] Missing character: " + charID);
continue;
@@ -190,34 +224,28 @@ package starling.text
if (charID == CHAR_SPACE || charID == CHAR_TAB)
lastWhiteSpace = i;
- var charImage:Image = bitmapChar.createImage();
-
if (kerning)
- currentX += bitmapChar.getKerning(lastCharID);
+ currentX += char.getKerning(lastCharID);
- charImage.x = currentX + bitmapChar.xOffset;
- charImage.y = bitmapChar.yOffset;
- charImage.color = color;
- currentLine.addChild(charImage);
+ charLocation = new CharLocation(char);
+ charLocation.x = currentX + char.xOffset;
+ charLocation.y = currentY + char.yOffset;
+ currentLine.push(charLocation);
- currentX += bitmapChar.xAdvance;
+ currentX += char.xAdvance;
lastCharID = charID;
- if (currentX > containerWidth)
+ if (charLocation.x + char.width > containerWidth)
{
// remove characters and add them again to next line
var numCharsToRemove:int = lastWhiteSpace == -1 ? 1 : i - lastWhiteSpace;
- var removeIndex:int = currentLine.numChildren - numCharsToRemove;
+ var removeIndex:int = currentLine.length - numCharsToRemove;
- for (var r:int=0; r<numCharsToRemove; ++r)
- currentLine.removeChildAt(removeIndex);
+ currentLine.splice(removeIndex, numCharsToRemove);
- if (currentLine.numChildren == 0)
+ if (currentLine.length == 0)
break;
- var lastChar:DisplayObject = currentLine.getChildAt(currentLine.numChildren-1);
- currentX = lastChar.x + lastChar.width;
-
i -= numCharsToRemove;
lineFull = true;
}
@@ -225,18 +253,20 @@ package starling.text
if (i == numChars - 1)
{
- lineContainer.addChild(currentLine);
+ lines.push(currentLine);
finished = true;
}
else if (lineFull)
{
- lineContainer.addChild(currentLine);
- var nextLineY:Number = currentLine.y + mLineHeight;
+ lines.push(currentLine);
+ currentY += mLineHeight;
- if (nextLineY + mLineHeight <= containerHeight)
+ if (lastWhiteSpace == i)
+ currentLine.pop();
+
+ if (currentY + mLineHeight <= containerHeight)
{
- currentLine = new Sprite();
- currentLine.y = nextLineY;
+ currentLine = new <CharLocation>[];
currentX = 0;
lastWhiteSpace = -1;
lastCharID = -1;
@@ -247,45 +277,51 @@ package starling.text
}
}
} // for each char
- } // if (mLineHeight * scale <= height)
+ } // if (mLineHeight <= containerHeight)
if (autoScale && !finished)
{
fontSize -= 1;
- lineContainer.dispose();
+ lines.length = 0;
}
else
{
finished = true;
}
-
} // while (!finished)
- if (hAlign != HAlign.LEFT)
- {
- var numLines:int = lineContainer.numChildren;
- for (var l:int=0; l<numLines; ++l)
- {
- var line:Sprite = lineContainer.getChildAt(l) as Sprite;
- var finalChar:DisplayObject = line.getChildAt(line.numChildren-1);
- var lineWidth:Number = finalChar.x + finalChar.width;
- var widthDiff:Number = containerWidth - lineWidth;
- line.x = int(hAlign == HAlign.RIGHT ? widthDiff : widthDiff / 2);
- }
- }
+ var finalLocations:Vector.<CharLocation> = new <CharLocation>[];
+ var numLines:int = lines.length;
+ var bottom:Number = currentY + mLineHeight;
+ var yOffset:int = 0;
- var outerContainer:Sprite = new Sprite();
- outerContainer.addChild(lineContainer);
+ if (vAlign == VAlign.BOTTOM) yOffset = containerHeight - bottom;
+ else if (vAlign == VAlign.CENTER) yOffset = (containerHeight - bottom) / 2;
- if (vAlign != VAlign.TOP)
+ for (var lineID:int=0; lineID<numLines; ++lineID)
{
- var contentHeight:Number = lineContainer.numChildren * mLineHeight * scale;
- var heightDiff:Number = height - contentHeight;
- lineContainer.y = int(vAlign == VAlign.BOTTOM ? heightDiff : heightDiff / 2);
+ var line:Vector.<CharLocation> = lines[lineID];
+ var lastLocation:CharLocation = line[line.length-1];
+ var right:Number = lastLocation.x + lastLocation.char.width;
+ var xOffset:int = 0;
+
+ if (hAlign == HAlign.RIGHT) xOffset = containerWidth - right;
+ else if (hAlign == HAlign.CENTER) xOffset = (containerWidth - right) / 2;
+
+ numChars = line.length;
+ for (var c:int=0; c<numChars; ++c)
+ {
+ charLocation = line[c];
+ charLocation.x = scale * (charLocation.x + xOffset)
+ charLocation.y = scale * (charLocation.y + yOffset);
+ charLocation.scale = scale;
+
+ if (charLocation.char.width > 0 && charLocation.char.height > 0)
+ finalLocations.push(charLocation);
+ }
}
- outerContainer.flatten();
- return outerContainer;
+ return finalLocations;
}
/** The name of the font as it was parsed from the font file. */
@@ -298,4 +334,17 @@ package starling.text
public function get lineHeight():Number { return mLineHeight; }
public function set lineHeight(value:Number):void { mLineHeight = value; }
}
+}
+
+class CharLocation
+{
+ public var char:starling.text.BitmapChar;
+ public var scale:Number;
+ public var x:Number;
+ public var y:Number;
+
+ public function CharLocation(char:starling.text.BitmapChar)
+ {
+ this.char = char;
+ }
}
View
65 starling/src/starling/text/TextField.as
@@ -18,6 +18,7 @@ package starling.text
import flash.text.TextFormat;
import flash.utils.Dictionary;
+ import starling.core.QuadBatch;
import starling.core.RenderSupport;
import starling.core.Starling;
import starling.display.DisplayObject;
@@ -78,9 +79,11 @@ package starling.text
private var mHitArea:DisplayObject;
private var mTextArea:DisplayObject;
- private var mContents:DisplayObject;
private var mBorder:DisplayObjectContainer;
+ private var mImage:Image;
+ private var mQuadBatch:QuadBatch;
+
// this object will be used for text rendering
private static var sNativeTextField:flash.text.TextField = new flash.text.TextField();
@@ -116,10 +119,8 @@ package starling.text
public override function dispose():void
{
removeEventListener(Event.FLATTEN, onFlatten);
-
- if (mContents is Image)
- (mContents as Image).texture.dispose();
-
+ if (mImage) mImage.texture.dispose();
+ if (mQuadBatch) mQuadBatch.dispose();
super.dispose();
}
@@ -137,24 +138,16 @@ package starling.text
private function redrawContents():void
{
- if (mContents)
- {
- if (mContents is Image)
- (mContents as Image).texture.dispose();
-
- mContents.removeFromParent(true);
- }
+ if (mIsRenderedText) createRenderedContents();
+ else createComposedContents();
- mContents = mIsRenderedText ? createRenderedContents() : createComposedContents();
- mContents.touchable = false;
mRequiresRedraw = false;
-
- addChild(mContents);
}
- private function createRenderedContents():DisplayObject
+ private function createRenderedContents():void
{
- if (mText.length == 0) return new Sprite();
+ if (mQuadBatch) { mQuadBatch.removeFromParent(true); mQuadBatch = null; }
+ if (mText.length == 0) { mImage.removeFromParent(true); mImage = null; return; }
var scale:Number = Starling.contentScaleFactor;
var width:Number = mHitArea.width * scale;
@@ -202,10 +195,20 @@ package starling.text
mTextArea.width = textWidth / scale;
mTextArea.height = textHeight / scale;
- var contents:Image = new Image(Texture.fromBitmapData(bitmapData, true, false, scale));
- contents.color = mColor;
+ var texture:Texture = Texture.fromBitmapData(bitmapData, true, false, scale);
- return contents;
+ if (mImage == null)
+ {
+ mImage = new Image(texture);
+ addChild(mImage);
+ }
+ else
+ {
+ mImage.texture = texture;
+ mImage.readjustSize();
+ }
+
+ mImage.color = mColor;
}
private function autoScaleNativeTextField(textField:flash.text.TextField):void
@@ -224,22 +227,26 @@ package starling.text
}
}
- private function createComposedContents():DisplayObject
+ private function createComposedContents():void
{
+ if (mImage) { mImage.removeFromParent(true); mImage = null; }
+ if (mQuadBatch == null) { mQuadBatch = new QuadBatch(); addChild(mQuadBatch); }
+ else mQuadBatch.reset();
+
+ if (mText.length == 0) return;
+
var bitmapFont:BitmapFont = sBitmapFonts[mFontName];
if (bitmapFont == null) throw new Error("Bitmap font not registered: " + mFontName);
- var contents:DisplayObject = bitmapFont.createDisplayObject(
+ bitmapFont.fillQuadBatch(mQuadBatch,
mHitArea.width, mHitArea.height, mText, mFontSize, mColor, mHAlign, mVAlign,
mAutoScale, mKerning);
- var textBounds:Rectangle = (contents as DisplayObjectContainer).bounds;
+ var textBounds:Rectangle = mQuadBatch.bounds;
mTextArea.x = textBounds.x;
mTextArea.y = textBounds.y;
mTextArea.width = textBounds.width;
mTextArea.height = textBounds.height;
-
- return contents;
}
private function updateBorder():void
@@ -341,10 +348,8 @@ package starling.text
mColor = value;
updateBorder();
- if (mContents is Image && mIsRenderedText)
- (mContents as Image).color = value;
- else
- mRequiresRedraw = true;
+ if (mImage) mImage.color = value;
+ else mRequiresRedraw = true;
}
}
View
8 starling/src/starling/utils/VertexData.as
@@ -269,18 +269,20 @@ package starling.utils
* If you pass a 'resultRect', the result will be stored in this rectangle
* instead of creating a new object. */
public function getBounds(transformationMatrix:Matrix=null,
+ vertexID:int=0, numVertices:int=-1,
resultRect:Rectangle=null):Rectangle
{
if (resultRect == null) resultRect = new Rectangle();
+ if (numVertices < 0) numVertices = mNumVertices;
var minX:Number = Number.MAX_VALUE, maxX:Number = -Number.MAX_VALUE;
var minY:Number = Number.MAX_VALUE, maxY:Number = -Number.MAX_VALUE;
- var offset:int = POSITION_OFFSET;
+ var offset:int = getOffset(vertexID) + POSITION_OFFSET;
var x:Number, y:Number, i:int;
if (transformationMatrix == null)
{
- for (i=0; i<mNumVertices; ++i)
+ for (i=vertexID; i<numVertices; ++i)
{
x = mRawData[offset];
y = mRawData[int(offset+1)];
@@ -294,7 +296,7 @@ package starling.utils
}
else
{
- for (i=0; i<4; ++i)
+ for (i=vertexID; i<numVertices; ++i)
{
x = mRawData[offset];
y = mRawData[int(offset+1)];
Please sign in to comment.
Something went wrong with that request. Please try again.