[转] BMPText与BMPSlicer——将位图切割成块来展示文字

[转] BMPText与BMPSlicer——将位图切割成块来显示文字
因为矢量图的表现力毕竟有限,因此我们经常需要使用外部的图片来显示文字等信息,最多的情况就是显示数字和字母。

但是,如果将每个数字或者字母做成一个图片,在程序运行过程中,就要载入许多的小文件,这样出错的几率就会变大,而且也会影响网络性能。因此,我写了两个类来处理这种情况。

例如:要使用0-9的数字图片,可以将0-9拼在一张长条形的图片中,载入后使用BMPSlicer来切割,切割后,就可以使用BMPText来显示它。

下图就是被切割的图片:

[转] BMPText与BMPSlicer——将位图切割成块来展示文字




package
{
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;

import org.zengrong.display.BMPText;


[SWF(width=200,height=100,backgroundColor=0xCCCCCC)]
public class BMPTextTest extends Sprite
{
        public function BMPTextTest()
        {
                _bmpText = new BMPText('0123456789', 46, 52, true, Bitmap(new _timerClass()).bitmapData);
                _bmpText.gap = -10;
                addChild(_bmpText);
                _timer = new Timer(100, 99999);
                _timer.addEventListener(TimerEvent.TIMER, handler_timer);
                _timer.start();
                this.addEventListener(Event.ADDED_TO_STAGE, handler_addToStage);
        }
        
        [Embed(source="timer.png")]
        private static var _timerClass:Class;
        
        private var _bmpText:BMPText;
        private var _timer:Timer;

        private function handler_addToStage(evt:Event=null):void
        {
                _bmpText.x = stage.stageWidth/2 - _bmpText.width/2;
                _bmpText.y = stage.stageHeight/2 - _bmpText.height/2;
        }
        
        private function handler_timer(evt:TimerEvent):void
        {
                _bmpText.text = _timer.currentCount.toString();
                handler_addToStage();
        }
}
}

////////////////////////////////////////////////////////////////////////////////
//
//  zengrong.net
//  创建者:        zrong
//  最后更新时间:2010-12-07
//
////////////////////////////////////////////////////////////////////////////////
package org.zengrong.display
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Sprite;


/**
 * 用位图拼成文字形式用于显示 
 * @author zrong
 */
public class BMPText extends Sprite
{
        /**
         * 设置所有能显示的字符范围,以及提供代替字符显示用的BitmapData数据。 
         * @param $allText 所有能显示的字符串
         * @param $width 字符图片的宽度
         * @param $height 字符图片的高度
         * @param $transparent 字符图片是否支持透明
         * @param $bmds 字符图片数据,可以为一个或多个。<br />
         * 每个参数都必须是BitmapData类型。<br />
         * 如果提供的字符图片数据小于$allText的字符数量,则仅使用第一个BitmapData进行切割。<br />
         * 否则按照提供的参数的数据与$allText字符串中的字符进行对应。
         */     
        public function BMPText($allText:String='', $width:int=-1, $height:int=-1, $transparent:Boolean=false, $direction:String='horizontal', ...$bmds:Array)
        {
                if($allText && $width>0 && $height>0 && $bmds.length>0)
                {
                        var __args:Array = [$allText, $width, $height, $transparent, $direction];
                        setBMPAndText.apply(this, __args.concat($bmds));
                }
        }
        
        private var _text:String;               //当前正在显示的字符
        private var _allText:String;    //支持显示的所有字符
        private var _gap:int = 0;               //文字间距
        private var _allTextLength:int; //支持显示的所有字符的数量
        private var _bmpWidth:int;              //一个文字位图的宽度
        private var _bmpHeight:int;             //一个文字位图的高度
        private var _transparent:Boolean;       //是否透明
        private var _direction:String;          //切割方向
        private var _slice:BMPSlicer;   //用来对长的位图进行切片
        private var _bmdList:Vector.<BitmapData>;       //支持显示的所有字符对应的BitmapData列表
        private var _textIndex:Object;                  //保存每个文字的索引
        
        //--------------------------------------
        //
        //  公有方法
        //
        //--------------------------------------
        
        //----------------------------------
        //  getter方法
        //----------------------------------
        /**
         * 获取当前正在显示的字符串。 
         */     
        public function get text():String
        {
                return _text;
        }
        
        /**
         * 获取允许显示的所有字符串。 
         */     
        public function get allText():String
        {
                return _allText;
        }
        
        public function get gap():int
        {
                return _gap;
        }
        
        //----------------------------------
        //  setter方法
        //----------------------------------
        /**
         * 设置文本位图之间的间隔。 
         * @param $gap 文本位图之间的间隔。
         */     
        public function set gap($gap:int):void
        {
                _gap = $gap;
                var __child:int = numChildren;
                for(var i:int=0; i<__child; i++)
                {
                        var __dis:DisplayObject = getChildAt(i);
                        __dis.x = i * _bmpWidth;
                        if(i > 0)
                                __dis.x += i*_gap;
                }
        }
        
        /**
         * 设置显示的文本。
         * @param $text 要显示的文本。
         * @throw RangeError 如果参数中的文本中有字符不在允许字符的范围内,就会抛出此异常。
         */     
        public function set text($text:String):void
        {
                while(numChildren>0)
                        removeChildAt(0);
                if(!_allText || !_bmdList)
                        return;
                _text = $text;
                var __length:int = _text.length;
                for(var i:int=0; i<__length; i++)
                {
                        var __curTxt:String = _text.charAt(i);
                        if(_allText.indexOf(__curTxt) == -1)
                                throw new RangeError('字符"'+__curTxt+'"不在允许的字符范围内。');
                        var __bmp:Bitmap = getTextBMP(__curTxt);
                        __bmp.x = i * _bmpWidth;
                        //如果不是第一个图片,就加上分隔宽度
                        if(i > 0)
                                __bmp.x += i*_gap;
                        addChild(__bmp);
                }
        }
        
        /**
         * @copy BMPText#BMPText()
         */     
        public function setBMPAndText($allText:String, $width:int, $height:int, $transparent:Boolean, $direction:String, ...$bmds:Array):void
        {
                if($allText.length <= 0)
                        return;
                if($bmds == null || $bmds.length == 0)
                        return;
                _allText = $allText;
                _allTextLength = _allText.length;
                _bmpWidth = $width;
                _bmpHeight = $height;
                _transparent = $transparent;
                _direction = $direction;
                
                _textIndex = {};
                //如果提供的BitmapData的数量小于文本数量,就用第一个BitmapData进行分割
                if($bmds.length < _allTextLength)
                {
                        var __bmd1:BitmapData = BitmapData($bmds[0]);
                        if(__bmd1.width < _allTextLength*_bmpWidth)
                                throw new RangeError('提供的BitmapData的宽度不够切割成'+_allTextLength+'块!');
                        _slice = new BMPSlicer(__bmd1, _bmpWidth, _bmpHeight, $transparent, $direction, _allTextLength);
                        _bmdList = _slice.slicedBitmapDataList;
                        for(var i:int=0; i<_allTextLength; i++)
                        {
                                //保存对应每个文字的图片的索引
                                _textIndex[_allText.charAt(i)] = i;
                        }
                }
                //否则就认为每个参数都是一个与文本对应的BitmapData
                else
                {
                        _bmdList = new Vector.<Bitmap>(_allTextLength, true);
                        for(var j:int=0; j<_allTextLength; j++)
                        {
                                _bmdList[j] = BitmapData($bmds[j]);
                                //保存对应每个文字的图片的索引
                                _textIndex[_allText.charAt(j)] = j;
                        }
                }
        }
        
        /**
         * 使用当前的数据复制一个BMPText
         */     
        public function duplicate():BMPText
        {
                var __bmpt:BMPText = new BMPText();
                var __param:Array = [_allText, _bmpWidth, _bmpHeight, _transparent, _direction];
                __bmpt.setBMPAndText.apply(__bmpt, __param.concat(_bmdList));
                __bmpt.gap = _gap;
                __bmpt.text = _text;
                return __bmpt;
        }
        
        private function getTextBMP($txt:String):Bitmap
        {
                var __index:int = _textIndex[$txt];
                return new Bitmap(_bmdList[__index]);
        }
}
}







////////////////////////////////////////////////////////////////////////////////
//
//  zengrong.net
//  创建者:        zrong
//  最后更新时间:2010-12-07
//
////////////////////////////////////////////////////////////////////////////////
package org.zengrong.display
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;

/**
 * 用于将一行宽度相同的连续位图按照固定的宽度和高度切成单独的块。
 * 例如:要使用0-9的数字图片,一个个载入比较浪费,可以将0-9拼在一张长条形的图片中,然后使用BMPSlicer来切割。
 * @author zrong
 */
public class BMPSlicer
{
        /**
         * 代表横向切割的常量 
         */     
        public static const VERTICAL:String = 'vertical';
        /**
         * 代表纵向切割的常量 
         */     
        public static const HORIZONTAL:String = 'horizontal';
        
        public static const ERROR_MSG_WIDTH:String = '请先定义切片的宽度。';
        public static const ERROR_MSG_HEIGHT:String = '请先定义切片的高度。';
        public static const ERROR_MSG_BITMAPDATA:String = '请先提供要切割的BitmapData。';
        
        /**
         * @param $bmd 要切割的BitmapData
         * @param $width 切片的宽度
         * @param $height 切片的高度
         * @param $transparent 切片是否透明
         * @param $direction 是横向切割还是纵向切割
         * @param $length 切片的数量
         */     
        public function BMPSlicer($bmd:BitmapData=null, $width:int=-1, $height:int=-1, $transparent:Boolean=true, $direction:String='horizontal', $length:int=-1)
        {
                _bmd = $bmd;
                _width = $width;
                _height = $height;
                _transparent = $transparent;
                _direction = $direction;
                _length = $length;
                if(_bmd && (_width>0) && (_height>0))
                        slice();
        }
        
        //--------------------------------------
        //
        //  实例变量
        //
        //--------------------------------------    
        protected var _bmd:BitmapData;
        protected var _width:int;
        protected var _height:int;
        protected var _transparent:Boolean;
        protected var _direction:String;
        protected var _length:int;
        
        /**
         * 保存切割好的BitmapData 
         */     
        protected var _bmdList:Vector.<BitmapData>;
        
        //--------------------------------------
        //
        //  getter方法
        //
        //--------------------------------------    
        
        /**
         * 返回切割好的BitmapData
         * @throw ReferenceError 如果没有执行过slice方法,就会抛出此异常。
         * @see #slice()
         */     
        public function get slicedBitmapDataList():Vector.<BitmapData>
        {
                if(!_bmdList)
                        throw new ReferenceError('切片列表还没有定义!请先执行BMPSlicer.slice方法。');
                return _bmdList;
        }
        
        /**
         * 根据索引返回要被切成片的BitmapData 
         * @param $index 切片在切片列表中的索引,从0开始
         */     
        public function getSlice($index:int):BitmapData
        {
                return _bmdList[$index];
        }

        /**
         * 根据索引返回被切成片的位图数据,并包装成Bitmap显示对象 
         * @param $index 切片在切片列表中的索引,从0开始
         */     
        public function getSlicedBMP($index:int):Bitmap
        {
                return new Bitmap(getSlice($index));
        }
        
        //--------------------------------------
        //
        //  setter方法
        //
        //--------------------------------------
        
        public function set bitmapData($bmd:BitmapData):void
        {
                _bmd = $bmd;
        }
        
        public function set width($width:int):void
        {
                _width = $width;
        }
        
        public function set height($height:int):void
        {
                _height = $height;
        }
        
        public function set transparent($transparent:Boolean):void
        {
                _transparent = $transparent;
        }
        
        public function set direction($direction:String):void
        {
                _direction = $direction;
        }
        
        public function set length($length:int):void
        {
                _length = $length;
        }
        
        //--------------------------------------
        //
        //  方法
        //
        //--------------------------------------
        /**
         * 执行切片动作 
         * @throw ReferenceError 如果必要的属性没有定义,就会抛出异常。
         */     
        public function slice():void
        {
                if(!_bmd)
                        throw new ReferenceError(ERROR_MSG_BITMAPDATA);
                if(_width < 0)
                        throw new ReferenceError(ERROR_MSG_WIDTH);
                if(_height < 0)
                        throw new ReferenceError(ERROR_MSG_HEIGHT);
                //如果没有传递$length值,就根据位图的宽度计算
                if(_length < 0)
                        _length = int(_bmd.width / _width);
                //根据长度建立一个固定长度的数组
                _bmdList = new Vector.<BitmapData>(_length, true);
                var __rect:Rectangle = new Rectangle(0, 0, _width, _height);
                var __pt:Point = new Point(0, 0);
                //新建一个BitmapData
                for(var i:int=0; i<_length; i++)
                {
                        var __newBmd:BitmapData = new BitmapData(_width, _height, _transparent);
                        if(_direction == HORIZONTAL)
                                __rect.x= i * _width;
                        else
                                __rect.y = i * _height;
                        __newBmd.copyPixels(_bmd, __rect, __pt);
                        _bmdList[i] = __newBmd;
                }
        }
}
}