テキストを滑らかに表示

Flashでデバイスフォントを使ったテキスト表示する場合、WindowsOS上ではアンチエイリアスがあまりかからない。
一度拡大した上でBitmapData上にDrawし、縮小をかけると滑らかなテキストの画像が得られる。
そこで、通常のTextFieldと比較しながら拡大率によるアンチエイリアスのかかり方の確認ができるようにした。

テキストを滑らかに表示 – wonderfl build flash online

Size:36、Scale:2の場合、
Normalのほうは36ptのテキストを表示のまま。
Scalingのほうは36×2=72ptのテキストとして、BitmapDataにdrawし、1/2に縮小してから表示用Bitmapにdrawしている。
縮小時、一気に1/2にするのではなく、1/√2を二回かけた方が綺麗な表示になった。
ただし、例えばTextFieldで最大127ptまで拡大できる場合、最初に二倍の解像度が必要なため、Scalingのほうは最大64pt程度までとなる。

package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	/**
	 * ...
	 * @author mztm
	 */
	[SWF(width = 465, height = 465, backgroundColor = 0xEEEEEE, frameRate = 30)]
	public class WonderflMain extends Sprite 
	{
		
		public function WonderflMain():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			
			graphics.beginFill(0xEEEEEE);
			graphics.drawRect(0, 0, 465, 465);
			graphics.endFill();
			
			addChild(new Canvas());
			
		}
		
	}
	
}

	import com.bit101.components.Label;
	import com.bit101.components.Style;
	import com.bit101.components.TextArea;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.PixelSnapping;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.DropShadowFilter;
	import flash.geom.Matrix;
	import flash.text.TextField;
	import flash.text.TextFormat;
    /**
     * ...
     * @author umhr
     */
    class Canvas extends Sprite
    {
        private var _textArea:TextArea;
        private var _layoutTF:TextField = new TextField();
        private var _normalTF:TextField = new TextField();
        private var _fontChooser:FontChooser;
		private var _bitmap:Bitmap = new Bitmap(new BitmapData(455, 110, true, 0x0), PixelSnapping.NEVER, true);
        public function Canvas()
        {
            init();
        }
        private function init():void
        {
            if (stage) onInit();
            else addEventListener(Event.ADDED_TO_STAGE, onInit);
        }

        private function onInit(event:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, onInit);
            // entry point

            Style.embedFonts = false;
            Style.fontName = "_等幅";
            Style.fontSize = 12;

            // 入力欄
            _textArea = new TextArea(this, 5, 5, "TextArea");
            _textArea.width = 455;
            _textArea.height = 40;
            _textArea.textField.type = "input";
            _textArea.addEventListener(Event.CHANGE, change);

            // フォント選択
            _fontChooser = new FontChooser();
			_fontChooser.x = 5;
			_fontChooser.y = 50;
            _fontChooser.addEventListener(Event.CHANGE, change);
            addChild(_fontChooser);
			
            // 流し込み結果
            _layoutTF.defaultTextFormat = new TextFormat(null, 14, null, false);
            _layoutTF.multiline = true;
            _layoutTF.autoSize = "left";
			
			var shape:Shape = new Shape();
			shape.graphics.beginFill(0xFFFFFF);
			shape.graphics.drawRect(0, 0, 455, 115);
			shape.graphics.drawRect(0, 135, 455, 115);
			shape.graphics.endFill();
			shape.x = 5;
			shape.y = 210;
			addChild(shape);
			
			new Label(this, 10, 195, "Normal(TextField)");
            _normalTF.defaultTextFormat = new TextFormat(null, 14, null, false);
            _normalTF.multiline = true;
            _normalTF.autoSize = "left";
            _normalTF.x = 10;
            _normalTF.y = 210;
            addChild(_normalTF);
			
			new Label(this, 10, 330, "Scaling(Bitmap)");
			_bitmap.x = 10;
			_bitmap.y = 345;
			addChild(_bitmap);
			
            _textArea.text = "ABCdefg1234\nあいうえおかきくけこ";
            change(null);
			
			//trace(_fontChooser.scale, Math.sqrt(_fontChooser.scale), 1 / Math.sqrt(_fontChooser.scale), (1 / Math.sqrt(_fontChooser.scale)) * (1 / Math.sqrt(_fontChooser.scale)));
        }

        private function change(event:Event):void
        {
			var scale:Number = _fontChooser.scale;
			
            _normalTF.defaultTextFormat = new TextFormat(_fontChooser.fontName, _fontChooser.fontSize, null, _fontChooser.isBold);
            _normalTF.text = _textArea.text;
			
            _layoutTF.defaultTextFormat = new TextFormat(_fontChooser.fontName, _fontChooser.fontSize * scale, null, _fontChooser.isBold);
            _layoutTF.text = _textArea.text;
			
			var tempBitmapData:BitmapData = new BitmapData(455 * scale, 110 * scale, true, 0x0);
			tempBitmapData.draw(_layoutTF, null, null, null, null, true);
			
			// 半分ずつリサイズしたほうが綺麗に表示される。
			var matrix:Matrix = new Matrix(1 / Math.sqrt(scale), 0, 0, 1 / Math.sqrt(scale));
			var tempBitmap:Bitmap = new Bitmap(new BitmapData(tempBitmapData.width, tempBitmapData.height, true, 0x00000000), PixelSnapping.NEVER, true);
			tempBitmap.bitmapData.draw(tempBitmapData, matrix, null, null, null, true);
			
			_bitmap.bitmapData.fillRect(_bitmap.bitmapData.rect, 0x0);
			_bitmap.bitmapData.draw(tempBitmap, matrix, null, null, null, true);
			
			_normalTF.filters = _fontChooser.isDropShadow?[new DropShadowFilter(4, 45, 0xCC3333)]:[];
			_bitmap.filters = _fontChooser.isDropShadow?[new DropShadowFilter(4, 45, 0xCC3333)]:[];
        }

    }

	
	import com.bit101.components.CheckBox;
	import com.bit101.components.Label;
	import com.bit101.components.List;
	import com.bit101.components.NumericStepper;
	import com.bit101.components.VScrollBar;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.text.Font;

    /**
     * ...
     * @author umhr
     */
    class FontChooser extends Sprite
    {
        public var fontName:String = "_等幅";
        public var fontSize:Number = 36;
		public var isBold:Boolean;
		public var isDropShadow:Boolean;
		public var scale:Number = 2;
		
        public function FontChooser()
        {
            init();
        }

        private function init():void
        {
			
            var list:com.bit101.components.List = new com.bit101.components.List(this, 0, 0, [ "_等幅", "_typewriter", "_ゴシック", "_sans", "_明朝", "_serif"]);
            list.selectedIndex = 0;
            list.width = 170;
            list.height = 140;
            list.addEventListener(Event.SELECT, list_select);

            var fontList:Array/*Font*/ = Font.enumerateFonts(true);
            var n:int = fontList.length;
            for (var i:int = 0; i < n; i++)
            {
                list.addItem(fontList[i].fontName);
            }

            var numericStepper:NumericStepper = new NumericStepper(this, 220);
            numericStepper.value = fontSize;
			numericStepper.step = 1;
            numericStepper.addEventListener(Event.CHANGE, numericStepper_select);
			new Label(this, 305, 0, "Size");
			
			new CheckBox(this, 220, 30, "Bold", onMouseClick);
			
            var scaleStepper:NumericStepper = new NumericStepper(this, 220, 50);
            scaleStepper.value = scale;
			scaleStepper.step = 0.1;
            scaleStepper.addEventListener(Event.CHANGE, scaleStepper_select);
			new Label(this, 305, 50, "Scale");
			
			new CheckBox(this, 220, 80, "DropShadow", onDropShadowClick);
			
        }
		
		private function onDropShadowClick(event:Event):void 
		{
			var checkBox:CheckBox = event.target as CheckBox;
			isDropShadow = checkBox.selected;
            dispatchEvent(new Event(Event.CHANGE));
		}
		
		private function onMouseClick(event:Event):void 
		{
			var checkBox:CheckBox = event.target as CheckBox;
			isBold = checkBox.selected;
            dispatchEvent(new Event(Event.CHANGE));
		}

        private function scaleStepper_select(event:Event):void
        {
            var numericStepper:NumericStepper = event.target as NumericStepper;
			if(numericStepper){
				scale = numericStepper.value;
				dispatchEvent(new Event(Event.CHANGE));
			}
        }

        private function numericStepper_select(event:Event):void
        {
            var numericStepper:NumericStepper = event.target as NumericStepper;
			if(numericStepper){
				fontSize = numericStepper.value;
				dispatchEvent(new Event(Event.CHANGE));
			}
        }

        private function list_select(event:Event):void
        {
            var list:com.bit101.components.List = (event.target as com.bit101.components.List);

            // 次の指定だとスクロールしてもの選択位置が移動しない。
            //fontName = _list.selectedItem.toString();

            var vScrollBar:VScrollBar = list.getChildAt(1) as VScrollBar;
            var num:int = list.selectedIndex + vScrollBar.value;
            fontName = list.items[num];
            dispatchEvent(new Event(Event.CHANGE));
        }

    }