2015年3月13日金曜日

Raspberry Pi + OpenVG でマルチバイト文字 本編

準備編ではラッパーライブラリ改変のための準備をしていきましたが、
この記事では実際にマルチバイト文字対応化していく手順を、
実際に私が行ったことを追っていくスタイルで説明していきます。

準備編はこちら



■配列のサイズを拡大

最初にラッパーライブラリ内の"256"をひたすら"65536"に変えてみました。
とりあえずSegmentaiton faultを防ぎたかったからです。
でも流石に65536はやり過ぎだったかと思ってます。
後述するcharacters.cを利用するのであれば文字関連の配列の大きさは4000あれば十分です。

■フォントにひらがなのデータを含める(失敗)

(openvg)/fontutil/font2openvg.cppの114行目くらいからちょこっと変更しました。
画像で失礼します

この時点では(あれ?これメチャクチャ簡単じゃね?)と思っていました…

結果、 ダメでした。
文字化けが起こりました。
(後で画像貼ります)

■文字コードのお話

知っている人は飛ばしてください
この時点で私が考えていた描画の流れはこんな感じです。
そもそもUnicodeを文字コードの一種だと思ってるのが間違い
実際は、こうでした。
"Unicodeのコードポイント"は、
"(UCS-4|UTF-32)の文字コード"かもしれません
つまり、[フォント名].incでは、文字のパスと、Unicodeのコードポイント(もしくはUCS-4の文字コード)が紐付けされた状態になっているのに対し、プログラムがlibshapes.cのTextメソッドに渡す文字の文字コードはUTF-8で、ASCII外の文字ではいわゆる文字化けが起こってしまう、ということのようです。
 そもそも私はUnicodeっていう文字コードがあるんだと本気で思ってた程、文字コードの知識が全く無かったので、文字コードに関していろいろな勘違いがありました。

こちらのページがわかりやすかったです。

文字コードの考え方から理解するUnicodeとUTF-8の違い
http://equj65.net/tech/charcode/

■UTF-8の文字コードをUnicodeのコードポイントに変換

下図のような仕組みでできるようですので、これに従ってコードを書き換えてみます。
ゴリ押しです。もっとスマートな方法があるのであれば教えて欲しいです。
char get_byte_length(char character){
    char mask = 0b10000000;
    char prefix = 0b00000000;
    char length = 0;
    while(length < 8){
        if((character & mask) == prefix) break;
        mask = mask >> 1;
        mask += 0b10000000;
        prefix = prefix >> 1;
        prefix += 0b10000000;
        length++;
    }
    printf("length:%d",length);
    return length;
}
 
// Text renders a string of text at a specified location, size, using the specified font glyphs
// derived from http://web.archive.org/web/20070808195131/http://developer.hybrid.fi/font2openvg/renderFont.cpp.txt
void Text(VGfloat x, VGfloat y, char *s, Fontinfo f, int pointsize) {
    VGfloat size = (VGfloat) pointsize, xx = x, mm[9];
    int i;
 
    vgGetMatrix(mm);
    for (i = 0; i < (int)strlen(s); ) {
 
        //unsigned int character = (unsigned int)s[i];
        char firstchar = *(s + i);
        char bytelength = get_byte_length(firstchar);
        int character = 0;
        switch (bytelength){
            case 0:
                i++;
                character = firstchar;
            break;
            case 2:
                character += (int)((*(s + i)) & 0b00011111) << 6;
                character += (int)(*(s + i + 1)) & 0b00111111;
                i += 2;
            break;
            case 3:
                character += (int)((*(s + i)) & 0b00001111) << 12;
                character += (int)((*(s + i + 1)) & 0b00111111) << 6;
                character += (int)((*(s + i + 2)) & 0b00111111);
                i += 3;
            break;
        }
 
        //printf("%x\n", s[i]);
        int glyph = f.CharacterMap[character];
        if (glyph == -1) {
            continue;              //glyph is undefined
        }
        VGfloat mat[9] = {
            size, 0.0f, 0.0f,
            0.0f, size, 0.0f,
            xx, y, 1.0f
        };
        vgLoadMatrix(mm);
        vgMultMatrix(mat);
        vgDrawPath(f.Glyphs[glyph], VG_FILL_PATH);
        xx += size * f.GlyphAdvances[glyph] / 65536.0f;
    }
    //printf("\n");
    vgLoadMatrix(mm);
}
TextWidthメソッドも同様に処理します。
この時点でひらがなは描画することができます。

■.incファイルに含める文字一覧を作成

ここで安直に、.incファイルに漢字も適当にぶち込んでみましょう。
真っ白な画面とともに、エラーコードVG_BAD_HANDLE_ERROR が帰ってくるはずです。 
フォントデータをVRAMか何処かに読み出すときに、あまりにフォントデータが大きい場合、フォントデータでVRAMが圧迫されてしまうのでしょうか。
 そもそも漢字はJIS第一水準が有れば十分なはずですので、.incファイルに含める文字(のUnicodeコードポイント)を配列にして、そこにない文字はパスを記録しないことにします。パスをパス

アルファベット、一部記号、ひらがな、JIS第一水準の漢字 これら3000字程度を集めたテキストファイルと、それを配列にした.cファイルを公開するので、どうぞお使いください。

https://gist.github.com/wararyo/1e3202bcf583f6bb8666 

ちなみに前者のテキストファイルから後者の.cファイルに変換するときは、HTMLエスケープ化した後適当に置換するとコーディングレスで作成できます。 記号も含めたい人は各自でどうぞ。 これを適用しましょう。


これでRaspberry Pi+OpenVGでマルチバイト文字が実現できるはずです。
お疲れ様でした。 

ちなみに私がこれを実現した時、深夜に一人テンションがMAXになり勢いのあまり近くにあったボイスレコーダーにキチガイ音声を録音してしまいました。
今聞いても完全に怪しい人です。

どうですか。美しくないですか。Raspberry Piが描画する日本語は! 
ぶっちゃけRaspberry PiのHDMI出力はなんかボヤボヤしててあんまり良いものじゃないですが、 自分が描画した日本語となれば自ずと綺麗に見えるのではないでしょうか。 
あ、見えないですか、そうですか…

質問がある方は気軽にコメントどうぞ。

それでは!

1 件のコメント:

  1. 2017 ford fusion hybrid titanium
    The 2017-2018 F1 hybrid titanium teeth dog is titanium automatic watch the complete reimagined version of the McLaren does titanium have nickel in it F1 engine – with a new design and titanium vs stainless steel apple watch a new lease of life. race tech titanium

    返信削除

Amazon