Skip to content

Instantly share code, notes, and snippets.

@tconkling
Created August 27, 2017 18:42
Show Gist options
  • Save tconkling/5e920fb2e1c32babfe7cbe3b924c58ac to your computer and use it in GitHub Desktop.
Save tconkling/5e920fb2e1c32babfe7cbe3b924c58ac to your computer and use it in GitHub Desktop.
Starling StyledText
//
// aciv
package aciv.text {
import starling.text.TextFormat;
public class StyledText {
public var text :String;
public var runs :Vector.<StyleRun> = new <StyleRun>[];
public var valid :Boolean;
public static function parse (string :String, defaultFormat :TextFormat, out :StyledText = null) :StyledText {
if (_parser == null) {
_parser = new StyledTextParser();
}
return _parser.parse(string, defaultFormat, out);
}
public function reset () :void {
this.text = null;
this.valid = false;
this.runs.length = 0;
}
private static var _parser :StyledTextParser;
}
}
import aciv.text.StyleRun;
import aciv.text.StyledText;
import aspire.util.Log;
import aspire.util.StringUtil;
import starling.text.TextFormat;
class StyledTextParser {
public function parse (string :String, defaultFormat :TextFormat, out :StyledText = null) :StyledText {
if (out == null) {
out = new StyledText();
} else {
out.reset();
}
_inString = string;
_defaultColor = defaultFormat.color;
out.text = "";
out.valid = parseInternal(out);
// If the input string was invalid, we just create a single StyleRun that encompasses the
// entire input string.
if (!out.valid) {
out.runs.length = 0;
out.text = _inString;
var singleRun :StyleRun = new StyleRun();
singleRun.length = _inString.length;
singleRun.color = defaultFormat.color;
out.runs[0] = singleRun;
}
reset();
return out;
}
/** Return true if parsing is successful */
private function parseInternal (st :StyledText) :Boolean {
if (TOKEN == null) {
TOKEN = new Token();
}
var tok :Token = eatNextToken(TOKEN);
for (; tok.type != Token.END; tok = eatNextToken(TOKEN)) {
if (tok.type == Token.POP_STYLE) {
if (!popStyle(st)) {
log.warning("Got a style close tag with no matching open tag", "string", _inString);
return false;
}
} else if (tok.type == Token.PUSH_STYLE) {
// parse our color
var newColor :uint;
try {
newColor = StringUtil.parseInteger(tok.value, 16);
} catch (e :Error) {
log.warning("Bad style tag; couldn't parse a color", "string", _inString, "tag", tok.value);
return false;
}
pushStyle(st, newColor);
} else {
appendText(st, tok.value);
}
}
if (_colorStack.length > 0) {
log.warning("Unclosed style tag", "string", _inString);
return false;
}
endCurRun(st);
return true;
}
private function eatNextToken (out :Token = null) :Token {
if (out == null) {
out = new Token();
}
if (_inIdx >= _inString.length) {
return out.reset(Token.END);
}
if (startsWith(_inString, STYLE_END, _inIdx)) {
_inIdx += STYLE_END.length;
return out.reset(Token.POP_STYLE);
}
STYLE_START.lastIndex = _inIdx;
var match :Object = STYLE_START.exec(_inString);
if (match != null && match.index == _inIdx) {
var completeMatch :String = match[0];
var style :String = match[1];
_inIdx += completeMatch.length;
return out.reset(Token.PUSH_STYLE, style);
}
var textEndIdx :int = _inString.indexOf("{", _inIdx + 1);
var text :String = (textEndIdx > _inIdx ?
_inString.substring(_inIdx, textEndIdx) :
_inString.substr(_inIdx));
_inIdx += text.length;
return out.reset(Token.TEXT, text);
}
private function pushStyle (st :StyledText, color :uint) :void {
_colorStack[_colorStack.length] = color;
beginNewRun(st);
}
private function popStyle (st :StyledText) :Boolean {
if (_colorStack.length > 0) {
_colorStack.pop();
beginNewRun(st);
return true;
} else {
return false;
}
}
private function beginNewRun (st :StyledText) :void {
var color :uint = (_colorStack.length > 0 ?
_colorStack[_colorStack.length -1] :
_defaultColor);
// we may be able to continue our current run
if (_curRun == null || _curRun.color != color) {
endCurRun(st);
_curRun = new StyleRun();
_curRun.color = color;
}
}
private function appendText (st :StyledText, text :String) :void {
if (_curRun == null) {
beginNewRun(st);
}
st.text += text;
_curRun.length += text.length;
}
private function endCurRun (st :StyledText) :void {
if (_curRun != null && _curRun.length > 0) {
st.runs[st.runs.length] = _curRun;
}
_curRun = null;
}
private function reset () :void {
_defaultColor = 0;
_inString = null;
_inIdx = 0;
_curRun = null;
_colorStack.length = 0;
}
private static function startsWith (str :String, substr :String, startIdx :int) :Boolean {
return str.indexOf(substr, startIdx) == startIdx;
}
// Internal parse state
private var _defaultColor :uint;
private var _inString :String;
private var _inIdx :int; // index into our input string
private var _curRun :StyleRun;
private const _colorStack: Vector.<uint> = new <uint>[];
private static const log :Log = Log.getLog(StyledTextParser);
private static var TOKEN :Token;
private static const STYLE_START :RegExp = /{style color=#(.*?)}/g;
private static const STYLE_END :String = "{/style}";
}
class Token {
public static const PUSH_STYLE :String = "PushStyle";
public static const POP_STYLE :String = "PopStyle";
public static const TEXT :String = "Text";
public static const END :String = "End";
public var type :String;
public var value :String;
public function reset (type :String, value :String = null) :Token {
this.type = type;
this.value = value;
return this;
}
}
//
// aciv
package aciv.text {
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.StyleSheet;
import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.DisplayObjectContainer;
import starling.display.MeshBatch;
import starling.display.Quad;
import starling.display.Sprite;
import starling.events.Event;
import starling.rendering.Painter;
import starling.styles.MeshStyle;
import starling.text.BitmapFont;
import starling.text.TextField;
import starling.text.TextFieldAutoSize;
import starling.text.TextFormat;
import starling.text.TextOptions;
import starling.utils.RectangleUtil;
public class StyledTextField extends DisplayObjectContainer
{
private var _text:String;
private var _options:TextOptions;
private var _format:TextFormat;
private var _textBounds:Rectangle;
private var _hitArea:Rectangle;
private var _font :BitmapFont;
private var _requiresRecomposition:Boolean;
private var _border:DisplayObjectContainer;
private var _meshBatch:MeshBatch;
private var _style:MeshStyle;
private var _recomposing:Boolean;
// helper objects
private static var sMatrix:Matrix = new Matrix();
/** Create a new text field with the given properties. */
public function StyledTextField(width:int, height:int, text:String="",
format:TextFormat=null, options:TextOptions=null)
{
_text = text ? text : "";
_hitArea = new Rectangle(0, 0, width, height);
_requiresRecomposition = true;
_format = format ? format.clone() : new TextFormat();
_format.addEventListener(Event.CHANGE, setRequiresRecomposition);
_options = options ? options.clone() : new TextOptions();
_options.addEventListener(Event.CHANGE, setRequiresRecomposition);
_meshBatch = new MeshBatch();
_meshBatch.touchable = false;
_meshBatch.pixelSnapping = true;
addChild(_meshBatch);
}
/** Disposes the underlying texture data. */
public override function dispose():void
{
_format.removeEventListener(Event.CHANGE, setRequiresRecomposition);
_options.removeEventListener(Event.CHANGE, setRequiresRecomposition);
_meshBatch.clear();
super.dispose();
}
/** @inheritDoc */
public override function render(painter:Painter):void
{
if (_requiresRecomposition) recompose();
super.render(painter);
}
/** Forces the text contents to be composed right away.
* Normally, it will only do so lazily, i.e. before being rendered. */
private function recompose():void
{
if (_requiresRecomposition)
{
_recomposing = true;
_meshBatch.clear();
var fontName:String = _format.font;
_font = BitmapFont(TextField.getCompositor(fontName));
if (_font == null && fontName == BitmapFont.MINI)
{
_font = new BitmapFont();
TextField.registerCompositor(_font, fontName);
}
updateText();
updateBorder();
_requiresRecomposition = false;
_recomposing = false;
}
}
// font and border rendering
private function updateText():void
{
var width:Number = _hitArea.width;
var height:Number = _hitArea.height;
// Horizontal autoSize does not work for HTML text, since it supports custom alignment.
// What should we do if one line is aligned to the left, another to the right?
if (isHorizontalAutoSize && !_options.isHtmlText) width = 100000;
if (isVerticalAutoSize) height = 100000;
_meshBatch.x = _meshBatch.y = 0;
_options.textureScale = Starling.contentScaleFactor;
StyledTextLayout.fillMeshBatch(_font, _meshBatch, width, height, _text, _format, _options);
if (_style) _meshBatch.style = _style;
if (_options.autoSize != TextFieldAutoSize.NONE)
{
_textBounds = _meshBatch.getBounds(_meshBatch, _textBounds);
if (isHorizontalAutoSize)
{
_meshBatch.x = _textBounds.x = -_textBounds.x;
_hitArea.width = _textBounds.width;
_textBounds.x = 0;
}
if (isVerticalAutoSize)
{
_meshBatch.y = _textBounds.y = -_textBounds.y;
_hitArea.height = _textBounds.height;
_textBounds.y = 0;
}
}
else
{
// hit area doesn't change, and text bounds can be created on demand
_textBounds = null;
}
}
private function updateBorder():void
{
if (_border == null) return;
var width:Number = _hitArea.width;
var height:Number = _hitArea.height;
var topLine:Quad = _border.getChildAt(0) as Quad;
var rightLine:Quad = _border.getChildAt(1) as Quad;
var bottomLine:Quad = _border.getChildAt(2) as Quad;
var leftLine:Quad = _border.getChildAt(3) as Quad;
topLine.width = width; topLine.height = 1;
bottomLine.width = width; bottomLine.height = 1;
leftLine.width = 1; leftLine.height = height;
rightLine.width = 1; rightLine.height = height;
rightLine.x = width - 1;
bottomLine.y = height - 1;
topLine.color = rightLine.color = bottomLine.color = leftLine.color = _format.color;
}
/** Forces the text to be recomposed before rendering it in the upcoming frame. Any changes
* of the TextField itself will automatically trigger recomposition; changes in its
* parents or the viewport, however, need to be processed manually. For example, you
* might want to force recomposition to fix blurring caused by a scale factor change.
*/
public function setRequiresRecomposition():void
{
if (!_recomposing)
{
_requiresRecomposition = true;
setRequiresRedraw();
}
}
// properties
private function get isHorizontalAutoSize():Boolean
{
return _options.autoSize == TextFieldAutoSize.HORIZONTAL ||
_options.autoSize == TextFieldAutoSize.BOTH_DIRECTIONS;
}
private function get isVerticalAutoSize():Boolean
{
return _options.autoSize == TextFieldAutoSize.VERTICAL ||
_options.autoSize == TextFieldAutoSize.BOTH_DIRECTIONS;
}
/** Returns the bounds of the text within the text field. */
public function get textBounds():Rectangle
{
if (_requiresRecomposition) recompose();
if (_textBounds == null) _textBounds = _meshBatch.getBounds(this);
return _textBounds.clone();
}
/** @inheritDoc */
public override function getBounds(targetSpace:DisplayObject, out:Rectangle=null):Rectangle
{
if (_requiresRecomposition) recompose();
getTransformationMatrix(targetSpace, sMatrix);
return RectangleUtil.getBounds(_hitArea, sMatrix, out);
}
/** @inheritDoc */
public override function hitTest(localPoint:Point):DisplayObject
{
if (!visible || !touchable || !hitTestMask(localPoint)) return null;
else if (_hitArea.containsPoint(localPoint)) return this;
else return null;
}
/** @inheritDoc */
public override function set width(value:Number):void
{
// different to ordinary display objects, changing the size of the text field should
// not change the scaling, but make the texture bigger/smaller, while the size
// of the text/font stays the same (this applies to the height, as well).
_hitArea.width = value / (scaleX || 1.0);
setRequiresRecomposition();
}
/** @inheritDoc */
public override function set height(value:Number):void
{
_hitArea.height = value / (scaleY || 1.0);
setRequiresRecomposition();
}
/** The displayed text. */
public function get text():String { return _text; }
public function set text(value:String):void
{
if (value == null) value = "";
if (_text != value)
{
_text = value;
setRequiresRecomposition();
}
}
/** The format describes how the text will be rendered, describing the font name and size,
* color, alignment, etc.
*
* <p>Note that you can edit the font properties directly; there's no need to reassign
* the format for the changes to show up.</p>
*
* <listing>
* var textField:TextField = new TextField(100, 30, "Hello Starling");
* textField.format.font = "Arial";
* textField.format.color = Color.RED;</listing>
*
* @default Verdana, 12 pt, black, centered
*/
public function get format():TextFormat { return _format; }
public function set format(value:TextFormat):void
{
if (value == null) throw new ArgumentError("format cannot be null");
_format.copyFrom(value);
}
/** The options that describe how the letters of a text should be assembled.
* This class basically collects all the TextField's properties that are needed
* during text composition. Since an instance of 'TextOptions' is passed to the
* constructor, you can pass custom options to the compositor. */
protected function get options():TextOptions { return _options; }
/** Draws a border around the edges of the text field. Useful for visual debugging.
* @default false */
public function get border():Boolean { return _border != null; }
public function set border(value:Boolean):void
{
if (value && _border == null)
{
_border = new Sprite();
addChild(_border);
for (var i:int=0; i<4; ++i)
_border.addChild(new Quad(1.0, 1.0));
updateBorder();
}
else if (!value && _border != null)
{
_border.removeFromParent(true);
_border = null;
}
}
/** Indicates whether the font size is automatically reduced if the complete text does
* not fit into the TextField. @default false */
public function get autoScale():Boolean { return _options.autoScale; }
public function set autoScale(value:Boolean):void { _options.autoScale = value; }
/** Specifies the type of auto-sizing the TextField will do.
* Note that any auto-sizing will implicitly deactivate all auto-scaling.
* @default none */
public function get autoSize():String { return _options.autoSize; }
public function set autoSize(value:String):void { _options.autoSize = value; }
/** Indicates if the text should be wrapped at word boundaries if it does not fit into
* the TextField otherwise. @default true */
public function get wordWrap():Boolean { return _options.wordWrap; }
public function set wordWrap(value:Boolean):void { _options.wordWrap = value; }
/** Indicates if TextField should be batched on rendering.
*
* <p>This works only with bitmap fonts, and it makes sense only for TextFields with no
* more than 10-15 characters. Otherwise, the CPU costs will exceed any gains you get
* from avoiding the additional draw call.</p>
*
* @default false
*/
public function get batchable():Boolean { return _meshBatch.batchable; }
public function set batchable(value:Boolean):void
{
_meshBatch.batchable = value;
}
/** Indicates if text should be interpreted as HTML code. For a description
* of the supported HTML subset, refer to the classic Flash 'TextField' documentation.
* Clickable hyperlinks and images are not supported. Only works for
* TrueType fonts! @default false */
public function get isHtmlText():Boolean { return _options.isHtmlText; }
public function set isHtmlText(value:Boolean):void { _options.isHtmlText = value; }
/** An optional style sheet to be used for HTML text. For more information on style
* sheets, please refer to the StyleSheet class in the ActionScript 3 API reference.
* @default null */
public function get styleSheet():StyleSheet { return _options.styleSheet; }
public function set styleSheet(value:StyleSheet):void { _options.styleSheet = value; }
/** The padding (in points) that's added to the sides of text that's rendered to a Bitmap.
* If your text is truncated on the sides (which may happen if the font returns incorrect
* bounds), padding can make up for that. Value must be positive. @default 0.0 */
public function get padding():Number { return _options.padding; }
public function set padding(value:Number):void { _options.padding = value; }
/** Controls whether or not the instance snaps to the nearest pixel. This can prevent the
* object from looking blurry when it's not exactly aligned with the pixels of the screen.
* @default true */
public function get pixelSnapping():Boolean { return _meshBatch.pixelSnapping; }
public function set pixelSnapping(value:Boolean):void { _meshBatch.pixelSnapping = value }
/** The mesh style that is used to render the text.
* Note that a style instance may only be used on one mesh at a time. */
public function get style():MeshStyle { return _meshBatch.style; }
public function set style(value:MeshStyle):void
{
_meshBatch.style = _style = value;
setRequiresRecomposition();
}
}
}
//
// aciv
package aciv.text {
import starling.display.Image;
import starling.display.MeshBatch;
import starling.text.BitmapChar;
import starling.text.BitmapFont;
import starling.text.TextFormat;
import starling.text.TextOptions;
import starling.utils.Align;
/** Adapted from starling.text.BitmapFont */
public class StyledTextLayout {
/** Draws text into a MeshBatch. */
public static function fillMeshBatch(font :BitmapFont, meshBatch:MeshBatch, width:Number, height:Number, text:String,
format:TextFormat, options:TextOptions=null):void
{
StyledText.parse(text, format, sStyledText);
var plainText :String = sStyledText.text;
var charLocations:Vector.<CharLocation> =
arrangeChars(font, width, height, plainText, format, options);
if (sHelperImage == null) {
sHelperImage = new Image(font.texture);
} else {
sHelperImage.texture = font.texture;
}
var curRunIdx :int = 0;
var curRun :StyleRun = sStyledText.runs[curRunIdx];
var curRunEnd :int = curRun.length;
var textLength :int = plainText.length;
var charIdx :int = 0;
for (var textIdx :int = 0; textIdx < textLength; ++textIdx) {
if (textIdx >= curRunEnd) {
curRun = sStyledText.runs[++curRunIdx];
curRunEnd += curRun.length;
}
// ArrangeChars omits non-printing characters from its output,
// so we skip over them here.
if (isNonPrintingChar(plainText.charCodeAt(textIdx))) {
continue;
}
var charLocation :CharLocation = charLocations[charIdx];
sHelperImage.color = curRun.color;
sHelperImage.texture = charLocation.char.texture;
sHelperImage.readjustSize();
sHelperImage.x = charLocation.x;
sHelperImage.y = charLocation.y;
sHelperImage.scale = charLocation.scale;
meshBatch.addMesh(sHelperImage);
charIdx++;
}
CharLocation.rechargePool();
sStyledText.reset();
}
private static function isNonPrintingChar (charCode :int) :Boolean {
switch (charCode) {
case CHAR_SPACE:
case CHAR_TAB:
case CHAR_NEWLINE:
case CHAR_CARRIAGE_RETURN:
return true;
default:
return false;
}
}
/** Arranges the characters of a text inside a rectangle, adhering to the given settings.
* Returns a Vector of CharLocations. */
private static function arrangeChars(font :BitmapFont, width:Number, height:Number, text:String,
format :TextFormat, options:TextOptions):Vector.<CharLocation>
{
if (text == null || text.length == 0) return CharLocation.vectorFromPool();
if (options == null) options = sDefaultOptions;
var kerning:Boolean = format.kerning;
var leading:Number = format.leading;
var hAlign:String = format.horizontalAlign;
var vAlign:String = format.verticalAlign;
var fontSize:Number = format.size;
var autoScale:Boolean = options.autoScale;
var wordWrap:Boolean = options.wordWrap;
var finished:Boolean = false;
var charLocation:CharLocation;
var numChars:int;
var containerWidth:Number;
var containerHeight:Number;
var scale:Number;
var i:int, j:int;
if (fontSize < 0) fontSize *= - font.size;
while (!finished)
{
sLines.length = 0;
scale = fontSize / font.size;
containerWidth = (width - 2 * font.padding) / scale;
containerHeight = (height - 2 * font.padding) / scale;
if (font.lineHeight <= containerHeight)
{
var lastWhiteSpace:int = -1;
var lastCharID:int = -1;
var currentX:Number = 0;
var currentY:Number = 0;
var currentLine:Vector.<CharLocation> = CharLocation.vectorFromPool();
numChars = text.length;
for (i=0; i<numChars; ++i)
{
var lineFull:Boolean = false;
var charID:int = text.charCodeAt(i);
var char:BitmapChar = font.getChar(charID);
if (charID == CHAR_NEWLINE || charID == CHAR_CARRIAGE_RETURN)
{
lineFull = true;
}
else if (char == null)
{
trace("[Starling] Font: "+ font.name + " missing character: " + text.charAt(i) + " id: "+ charID);
}
else
{
if (charID == CHAR_SPACE || charID == CHAR_TAB)
lastWhiteSpace = i;
if (kerning)
currentX += char.getKerning(lastCharID);
charLocation = CharLocation.instanceFromPool(char);
charLocation.x = currentX + char.xOffset;
charLocation.y = currentY + char.yOffset;
currentLine[currentLine.length] = charLocation; // push
currentX += char.xAdvance;
lastCharID = charID;
if (charLocation.x + char.width > containerWidth)
{
if (wordWrap)
{
// when autoscaling, we must not split a word in half -> restart
if (autoScale && lastWhiteSpace == -1)
break;
// remove characters and add them again to next line
var numCharsToRemove:int = lastWhiteSpace == -1 ? 1 : i - lastWhiteSpace;
for (j=0; j<numCharsToRemove; ++j) // faster than 'splice'
currentLine.pop();
if (currentLine.length == 0)
break;
i -= numCharsToRemove;
}
else
{
if (autoScale) break;
currentLine.pop();
// continue with next line, if there is one
while (i < numChars - 1 && text.charCodeAt(i) != CHAR_NEWLINE)
++i;
}
lineFull = true;
}
}
if (i == numChars - 1)
{
sLines[sLines.length] = currentLine; // push
finished = true;
}
else if (lineFull)
{
sLines[sLines.length] = currentLine; // push
if (lastWhiteSpace == i)
currentLine.pop();
if (currentY + leading + 2 * font.lineHeight <= containerHeight)
{
currentLine = CharLocation.vectorFromPool();
currentX = 0;
currentY += font.lineHeight + leading;
lastWhiteSpace = -1;
lastCharID = -1;
}
else
{
break;
}
}
} // for each char
} // if (font.lineHeight <= containerHeight)
if (autoScale && !finished && fontSize > 3)
fontSize -= 1;
else
finished = true;
} // while (!finished)
var finalLocations:Vector.<CharLocation> = CharLocation.vectorFromPool();
var numLines:int = sLines.length;
var bottom:Number = currentY + font.lineHeight;
var yOffset:int = 0;
if (vAlign == Align.BOTTOM) yOffset = containerHeight - bottom;
else if (vAlign == Align.CENTER) yOffset = (containerHeight - bottom) / 2;
for (var lineID:int=0; lineID<numLines; ++lineID)
{
var line:Vector.<CharLocation> = sLines[lineID];
numChars = line.length;
if (numChars == 0) continue;
var xOffset:int = 0;
var lastLocation:CharLocation = line[line.length-1];
var right:Number = lastLocation.x - lastLocation.char.xOffset
+ lastLocation.char.xAdvance;
if (hAlign == Align.RIGHT) xOffset = containerWidth - right;
else if (hAlign == Align.CENTER) xOffset = (containerWidth - right) / 2;
for (var c:int=0; c<numChars; ++c)
{
charLocation = line[c];
charLocation.x = scale * (charLocation.x + xOffset + font.offsetX) + font.padding;
charLocation.y = scale * (charLocation.y + yOffset + font.offsetY) + font.padding;
charLocation.scale = scale;
if (charLocation.char.width > 0 && charLocation.char.height > 0)
finalLocations[finalLocations.length] = charLocation;
}
}
return finalLocations;
}
// helper objects
private static var sHelperImage :Image;
private static var sStyledText :StyledText = new StyledText();
private static var sLines:Array = [];
private static var sDefaultOptions:TextOptions = new TextOptions();
private static const CHAR_SPACE:int = 32;
private static const CHAR_TAB:int = 9;
private static const CHAR_NEWLINE:int = 10;
private static const CHAR_CARRIAGE_RETURN:int = 13;
}
}
import starling.text.BitmapChar;
/** Coped from starling.text.BitmapFont */
class CharLocation
{
public var char:BitmapChar;
public var scale:Number;
public var x:Number;
public var y:Number;
public function CharLocation(char:BitmapChar)
{
reset(char);
}
private function reset(char:BitmapChar):CharLocation
{
this.char = char;
return this;
}
// pooling
private static var sInstancePool:Vector.<CharLocation> = new <CharLocation>[];
private static var sVectorPool:Array = [];
private static var sInstanceLoan:Vector.<CharLocation> = new <CharLocation>[];
private static var sVectorLoan:Array = [];
public static function instanceFromPool(char:BitmapChar):CharLocation
{
var instance:CharLocation = sInstancePool.length > 0 ?
sInstancePool.pop() : new CharLocation(char);
instance.reset(char);
sInstanceLoan[sInstanceLoan.length] = instance;
return instance;
}
public static function vectorFromPool():Vector.<CharLocation>
{
var vector:Vector.<CharLocation> = sVectorPool.length > 0 ?
sVectorPool.pop() : new <CharLocation>[];
vector.length = 0;
sVectorLoan[sVectorLoan.length] = vector;
return vector;
}
public static function rechargePool():void
{
var instance:CharLocation;
var vector:Vector.<CharLocation>;
while (sInstanceLoan.length > 0)
{
instance = sInstanceLoan.pop();
instance.char = null;
sInstancePool[sInstancePool.length] = instance;
}
while (sVectorLoan.length > 0)
{
vector = sVectorLoan.pop();
vector.length = 0;
sVectorPool[sVectorPool.length] = vector;
}
}
}
//
// aciv
package aciv.text {
public class StyleRun {
public var length :uint;
public var color :uint;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment