2017-06-06

[Flash] Starling Bitmap Font

[Flash] Starlingの学習 Bitmap Font

EmbeddedFont埋め込みフォントとFontForge文字数低減とBitmapFontGenerator(BMFont)

環境:

  • Starling 2.1
  • Adobe AIR SDK 22
  • FlashDevelop 5.2
  • Windows 7 64bit
  • Bitmap Font Genarator 1.13
  • FontForge cygwin 20160526

[話者] Starlingのフォント表示について、書籍『Introducing Starling』で学ぶぞ。

display fonts

  • アウトラインフォント(TrueType) ローカル
  • アウトラインフォント(TrueType) ローカル 太字(Bold)
  • アウトラインフォント(TrueType) 埋め込み
  • ビットマップフォント スムージングあり
  • ビットマップフォント スムージングなし

の5種類を表示する。

Main.as は前回と同じ。

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import starling.core.Starling;
    
    /**
     * ...
     * @author foo
     */
    [SWF(width="480",height="480",frameRate="60",backgroundColor="#002143")]
    public class Main extends Sprite 
    {
        private var stln:Starling;
        public static var frameRate:uint = 0;
        
        public function Main() 
        {
            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
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3dCreated);
            stln = new Starling(Game, stage);
            stln.showStats = true;
            stln.antiAliasing = 0;
            stln.start();
        }
        
        private function onContext3dCreated(e:Event):void 
        {
            stage.stage3Ds[0].removeEventListener(Event.CONTEXT3D_CREATE, onContext3dCreated);

            if (Starling.context.driverInfo.toLowerCase().indexOf("software") != -1) {
                // GPU disable
                Starling.current.nativeStage.frameRate = 30;
            } else {
                // GPU enable
            }
            frameRate = Starling.current.nativeStage.frameRate;
        }
    }
}

Game.as

package 
{
    import flash.display.Bitmap;
    import flash.text.Font;
    import starling.display.Sprite;
    import starling.events.Event;
    import starling.text.BitmapFont;
    import starling.text.TextField;
    import starling.text.TextFormat;
    import starling.textures.Texture;
    
    /**
     * ...
     * @author foo
     */
    public class Game extends Sprite 
    {
        [Embed(source = "../bin/circle-mplus-1p-regular-mini.ttf", embedAsCFF = "false", fontName = "CircleMplus")]
        public static var CircleMplus:Class;
        
        [Embed(source = "../bin/pixelmplus.png")]
        public static var PixelMplusGlyph:Class;

        [Embed(source = "../bin/pixelmplus.fnt", mimeType="application/octet-stream")]
        public static var PixelMplusXML:Class;
        
        [Embed(source = "../bin/pixelmplus12.png")]
        public static var PixelMplus12Glyph:Class;

        [Embed(source = "../bin/pixelmplus12.fnt", mimeType="application/octet-stream")]
        public static var PixelMplus12XML:Class;

        public function Game() 
        {
            if (stage) init()
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            addEventListener(Event.ENTER_FRAME, onGetGpuStyle);
        }
        
        private function onGetGpuStyle(e:Event):void  
        {
            if (Main.frameRate <= 0) return;
            removeEventListener(Event.ENTER_FRAME, onGetGpuStyle);
            
            addItem();
        }
        
        private function addItem():void 
        {
            var legend:TextField;
            var tft:TextFormat;

            legend = new TextField(
                180, 80,
                "フォント表示\nSystem font",
                new TextFormat("MS Mincho", 24, 0xFFFFFF)
            );
            legend.x = (stage.stageWidth - legend.width) / 2;
            legend.y = (stage.stageHeight / 5) * 0;
            addChild(legend);
            
            tft = new TextFormat("MS Mincho", 24, 0xFFFFFF);
            tft.color = 0xFF9999;
            tft.bold = true;
            legend = new TextField(
                180, 80,
                "フォント表示\nSystem font",
                tft
            );
            legend.border = true;
            legend.x = (stage.stageWidth - legend.width) / 2;
            legend.y = (stage.stageHeight / 5) * 1;
            addChild(legend);
            
            var font:Font = new CircleMplus();
            legend = new TextField(
                180, 80,
                "フォント表示\nEmbedded font",
                new TextFormat(font.fontName, 24, 0xFFFFFF)
            );
            legend.x = (stage.stageWidth - legend.width) / 2;
            legend.y = (stage.stageHeight / 5) * 2;
            addChild(legend);
            
            var bitmapChars:Bitmap;
            bitmapChars = new PixelMplusGlyph();
            var texture:Texture;
            texture = Texture.fromBitmap(bitmapChars);
            var xml:XML;
            xml = XML(new PixelMplusXML());
            TextField.registerBitmapFont(new BitmapFont(texture, xml));
            tft = new TextFormat("PixelMplus12Smoothing", BitmapFont.NATIVE_SIZE * 2, 0xFFFFFF);
            //tft = new TextFormat("PixelMplus12Smoothing", 24, 0xFFFFFF);
            //tft.size = BitmapFont.NATIVE_SIZE * 2;
            legend = new TextField(
                180, 80,
                "フォント表示\nBitmap font Smoothing",
                tft
            );
            legend.x = (stage.stageWidth - legend.width) / 2;
            legend.y = (stage.stageHeight / 5) * 3;
            addChild(legend);

            bitmapChars = new PixelMplus12Glyph();
            texture = Texture.fromBitmap(bitmapChars);
            xml = XML(new PixelMplus12XML());
            TextField.registerBitmapFont(new BitmapFont(texture, xml));
            //tft = new TextFormat("PixelMplus12", 24, 0xFFFFFF);
            legend = new TextField(
                180, 80,
                "フォント表示\nBitmap font NonSmoothing"
            );
            legend.format.font = "PixelMplus12";
            legend.format.size = BitmapFont.NATIVE_SIZE * 2;
            legend.format.color = 0xFFFFFF;
            legend.x = (stage.stageWidth - legend.width) / 2;
            legend.y = (stage.stageHeight / 5) * 4;
            addChild(legend);
        }
    }
}

実Flash。操作不可。

[合いの手] アウトラインフォント(TrueType) ローカル というのは、ここでは動作させているOS(Windows等)にインストール済みのフォントのことだね。

[話者] そうだ。「MS明朝」を指定しているので、それが入っていないOS(Mac, Linux等)では別のフォントで表示されるだろうな。

追記:macOS 10.10だと以下のように表示。

[合いの手] アウトラインフォント(TrueType) 埋め込み というのは?

[話者] swf(Flashファイル)にTrueTypeフォントを埋め込むのだ(埋め込まず外部ファイルとして読み込むのもアリらしい)。動作OSにフォントが入っていようがいまいが関係なく、意図したフォントで表示できるぞ。ただし日本語TrueTypeフォントは容量が大きいので、ここでは搭載文字数を少なくすることで容量を低減している(1.8MB→0.05MB)。やり方は後述。

[合いの手] ビットマップフォント スムージングあり/なし というのは?

[話者] PNG画像ファイルに文字を並べて、それをフォントとして扱うのだ。どの文字がどこにあるかの情報をXMLファイルで示す。これの作り方も後述。

reducing TrueType font glyph

TrueTypeフォントの文字数を減らす方法は、以下を実行した。

まず、TrueTypeフォントに含める文字すべてをリストアップ。文字が重複しててもかまわない。BOMありUTF-8テキストファイルで保存する。

char.txt

 !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789フォント表示 !″#$%&′()*+、-。/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_‘abcdefghijklmnopqrstuvwxyz{|}~ー「」

このテキストファイルから、node.jsを使用して、FontForgeスクリプトを作成。 charFF.txt というファイルを出力する。

listCharFF.js

// usage: $ node listCharFF.js char.txt
// テキストファイルに含まれる文字だけ残したフォントを作るためのFontForgeスクリプトを生成する

var fs = require('fs');
var text = fs.readFileSync(process.argv[2])+''.replace(new RegExp("\uFEFF",'g'),'');

var unique = function(array){
  var arr = array.sort();
  if (arr.length <= 0) return [];
  var uniq = [arr[0]];
  for(var i=1; i<arr.length; i++){
    if (arr[i] !== arr[i-1]){
      uniq.push(arr[i]);
    }
  }
  return uniq;
};

var chars = [];
for (var i=0; i<text.length; i++){
  var c = text.charAt(i);
  chars.push(c);
}

uniqchars = unique(chars);

var result = 'SelectWorthOutputting();\n';
for (var i=0; i<uniqchars.length; i++){
  //if (uniqchars[i].charCodeAt(0) < 128) continue; //改行コードと半角英数字は含めない
  result += 'SelectFewer(0u'+uniqchars[i].charCodeAt(0).toString(16)+');\n';
}
result += 'DetachAndRemoveGlyphs();';
//console.log(result);
fs.writeFileSync('charFF.txt', result);

このスクリプトは http://itouhiro.hatenablog.com/entry/20140511/font で公開しているものと同じだが、minttyだとconsole.log()がうまく動作しない。あと半角英数字を消す仕様になっていて今回はこまる。そこを修正した。

charFF.txt はこんな感じ。

SelectWorthOutputting();
SelectFewer(0u20);
SelectFewer(0u21);
SelectFewer(0u22);
SelectFewer(0u23);
SelectFewer(0u24);
SelectFewer(0u25);
...
SelectFewer(0uff56);
SelectFewer(0uff57);
SelectFewer(0uff58);
SelectFewer(0uff59);
SelectFewer(0uff5a);
DetachAndRemoveGlyphs();

FontForge最新版を入れて設定。

  • http://www.geocities.jp/meir000/fontforge/ から fontforge-cygwin_2016_05_26.zip 取得。
  • C:\home\bin\fontforge-cygwin_2016_05_26 に展開。
  • fontforge.bat ダブルクリックで起動。フォントファイルを開くダイアログが出てきたが[Cancel]。
  • ttfファイルは https://ja.osdn.net/projects/mix-mplus-ipa/releases/p12907 からCircle M+の最新を取得した。これは改変を加えたフォントを公開してもライセンス問題のない無料フォントだ。 circle-mplus-1p-regular.ttf
  • ttfファイルを C:\home\bin\fontforge-cygwin_2016_05_26 に配置して、fontforge.batにD&Dしてファイルを開く。何もせず閉じる。これで C:\home\bin\fontforge-cygwin_2016_05_26\FontForge\prefs ファイルが生成される。
  • C:\home\bin\fontforge-cygwin_2016_05_26\FontForge\prefs を書き換え。
--- /c/home/bin/fontforge-cygwin_2016_05_26/FontForge/prefs.orig        2017-06-04 21:28:31.873436800 +0900
+++ /c/home/bin/fontforge-cygwin_2016_05_26/FontForge/prefs     2017-06-04 21:51:06.726930000 +0900
@@ -128,3 +128,3 @@
 StarPercent:   1.73205
-CoverageFormatsAllowed:        3
+CoverageFormatsAllowed:        1
 DebugWins:     3

FontForgeでttfファイルを読み込み、スクリプトを実行して、TrueTypeフォントとして保存。

  • ttfファイルを C:\home\bin\fontforge-cygwin_2016_05_26 に配置して、fontforge.batにD&D。
  • [ファイル-スクリプト実行]でFFをチェック。charFF.txtをコピペ。[OK]
  • [エレメント-フォント情報-PS Names]の ファミリー名 表示用の名前 を修正してminiと追加。[TTF名]はかってに変わってたので特に修正せず。
  • [ファイル-フォントを出力]でTrueTypeを選ぶ。[オプション]は特に修正すべき点はない。Validate before Savingは遅いので外してよい。フォント名はminiとつけとく。
  • Warning表示されたけど生成はできた。 circle-mplus-1p-regular-mini.ttf

make PNG image font and XML file

ビットマップフォントを作る方法は http://d.hatena.ne.jp/nakamura001/20120910/1347241168 のとおりに実行した。

  • PixelMplus12 http://mix-mplus-ipa.osdn.jp/mplus/#pixelmplus をOSにインストールしておく。
  • Bitmap Font Generator(BMFont) http://www.angelcode.com/products/bmfont/ をインストール。
  • 起動して[Options→Font Settings]

    font:PixelMplus12
    Size 12 
    [v] Match char height
    [v] Render from TrueType outline ←ここをチェックするとスムージングなし。外すとスムージングがかかる
  • 次に[Options ExportSetting]

    Texture width,height: 2048 2048
    bitdepth: 32
    Preset: white text with alpha
    font description: XML
    texture: png - Porta..
  • 次にBOMつきUTF8のテキストファイルを読み込み。

    [Edit→Select Chars from file] char.txt
  • 保存。

    [Edit→Save bitmap font as] pixelplus

0 件のコメント:

コメントを投稿

人気記事