[BlueLeaf1336]> PROBLEMS> FontStudy>

フォント勉強会その3

historyTOP

2009/12/03:作成

GGO_GRAY4_BITMAP の場合TOP

さらにいってみます。しかし、なんでこのテストのときにフォームのフォントサイズを100ptにしたんだろう。愚か。

04 04 04 04 04 04 04 04 04 04 04 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 04 04 04 04 04 04 04 04 04 04 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 04 04 04 04 04 04 04 04 04 04 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00 10 10 10 10 10 10 10 10 10 10 10 00

もうここまできたら何回でも書くことにします。ブラウザの幅を狭くしてみると「i」が見えてきます、と。

同じくやってみます。

04 04 04 04 04 04 04 04 04 04 04 00 (1回)
10 10 10 10 10 10 10 10 10 10 10 00 (13回)
04 04 04 04 04 04 04 04 04 04 04 00 (1回)
00 00 00 00 00 00 00 00 00 00 00 00 (15回)
04 04 04 04 04 04 04 04 04 04 04 00 (1回)
10 10 10 10 10 10 10 10 10 10 10 00 (71回)

12個(12バイト)ずつ分けると102行。Metrics.gmBlackBoxY と同じ。PlatformSDKのいうとおり、GGO_GRAYn_BITMAPのフォーマットは同じようです。多分この勢いで少しずつにじむというかアンチエイリアス具合がパワーアップしてくようです。

じゃあ次は、どうやってこのデータを楽に画像に変えるのか? です。モノクロの場合 (GGO_BITMAP)は、Windows.TBitmapを介して、CreateBitmapIndirectにそのまま渡せるわけで、GGO_GRAYn_BITMAPでも何とかなるんじゃないの? と思うわけで。

今の知識でもっともストレートにいくなら、バイト配列の中身を Graphics.TBitmap.Canvas.Pixels (もしくはScanLine) に転記していけばいいんですが、できればこう、ピッとやりたいもんです。まあ、自前でやる方が融通が利きそうな気もしますけど。

CreateDIBSectionTOP

ふたたびテキストを読んでみました。どうもCreateDIBSectionが使えそうな気がします。他はあんまり読まずに書きますが、CreateDIBSection関数の引数の1つTBitmapInfo.bmiHeader.biBitCount8にセットすると、「このビットマップは最大で256色を持ち、ビットマップの各1バイトで1ピクセルを表」すと書いてます。いい感じです。

//-----------------------------------------------------------------------------
procedure TForm1.Button2Click(Sender: TObject);
var
    LDib: HBITMAP;
    LDibInfo: PBitmapInfo;
    LBitsPtr: PByte;
    LRefDC: HDC;
    i: Integer;
    //  多分宣言するだけでそれなりの中身になる(初期値がある。んだと思う)
    LSystemPalette: array[Byte] of TPaletteEntry;
begin
    //  ■ 1 ■ 前準備

    //  ビットマップ情報のデータ構造体に必要なメモリを確保
    GetMem(LDibInfo, SizeOf(TBitMapInfo) + 256 * SizeOf(TRGBQuad));

    //  TPaletteEntry と TRGBQuad の間の差を調整(理解する気なし)
    for i := Low(Byte) to High(Byte) do
    begin
        LDibInfo^.bmiColors[i].rgbBlue      := LSystemPalette[i].peBlue;
        LDibInfo^.bmiColors[i].rgbRed       := LSystemPalette[i].peRed;
        LDibInfo^.bmiColors[i].rgbGreen     := LSystemPalette[i].peGreen;
        LDibInfo^.bmiColors[i].rgbReserved  := 0;
    end;

    //  ビットマップ情報の初期化
    LDibInfo^.bmiHeader.biWidth         := 64;      //  幅
    LDibInfo^.bmiHeader.biHeight        := -64;     //  高さ(向き)
    LDibInfo^.bmiHeader.biPlanes        := 1;
    LDibInfo^.bmiHeader.biBitCount      := 8;       //  2^8 = 256色
    LDibInfo^.bmiHeader.biCompression   := BI_RGB;  //  圧縮なし
    LDibInfo^.bmiHeader.biSizeImage     := 0;       //  Windows任せ
    LDibInfo^.bmiHeader.biXPelsPerMeter := 0;
    LDibInfo^.bmiHeader.biYPelsPerMeter := 0;
    LDibInfo^.bmiHeader.biClrUsed       := 0;
    LDibInfo^.bmiHeader.biClrImportant  := 0;
    LDibInfo^.bmiHeader.biSize          := SizeOf(TBitMapInfoHeader);

    //  DIBを作成。ビットマップ情報も初期化
    LRefDC := CreateCompatibleDC(0);
    LDib := CreateDIBSection(
                LRefDC,             //  [in]参考にするデバイスコンテキスト
                LDibInfo^,          //  [in]今から作るビットマップの情報
                DIB_RGB_COLORS,     //  [in]TBitMapInfo.bmiColors[]はRGB値そのもの
                Pointer(LBitsPtr),  //  [out]作成されたビットマップビットのポインタ
                0,                  //  [in]ゼロならWindows任せ
                0);                 //  [in]上とセット。上がゼロなら無視される
    DeleteDC(LRefDC);

    //  ■ 2 ■ データ作成

    //  ビットマップの中身を描く
    FillMemory(LBitsPtr, 64 * 32, $01);
    FillMemory(Pointer(LongInt(LBitsPtr) + 64 * 32), 64 * 32, $05);

    //  ■ 3 ■ 描画

    //  フォームに描画
    SetDIBitsToDevice(Self.Canvas.Handle,
            90,                                 //  描画する横位置
            90,                                 //  描画する縦位置
            LDibInfo^.bmiHeader.biWidth,        //  幅
            - LDibInfo^.bmiHeader.biHeight,     //  高さ
            0,                                  //  SrcX
            0,                                  //  SrcY
            0,                                  //  走査開始行
            - LDibInfo^.bmiHeader.biHeight,     //  行数
            LBitsPtr,                           //  ビットマップの内容
            LDibInfo^,                          //  TBitmapInfo
            DIB_RGB_COLORS);                    //  CreateDIBSection と同じもの

    //  ■ 4 ■ 後始末

    //  メモリ解放
    DeleteObject(LDib);
    FreeMem(LDibInfo, SizeOf(TBitMapInfo) + 256 * SizeOf(TRGBQuad));
end;

テキストに書いてあったそのままをうつしてから、後のことを考えて少しだけいじってます。

で、こうなります。

流れとしては、邪魔くさい前準備、邪魔くさいデータ作成、邪魔くさい描画、後始末の4段階です。このうち、前準備は邪魔くさいといっても幅と高さ以外はほっとけばよさそうです。描画もFormに描くならこのまま、後始末はこれでよし。

で、データ作成の部分。

ここに、GetGlyphOutlineで得られたバイト配列を使えないかと。いや、使えるだろうと。その前に少しテストを。

CreateDIBSection と GetGlyphOutline のコラボレーションの1歩前TOP

宣言部分にバイト配列を準備します。サイズは、GGO_GRAY2_BITMAP を試したときのサイズです。

    //  バイト配列
    LBuf: array[0..1224-1] of Byte;

前準備の中で、画像の幅と高さを変更します。

    //  ビットマップ情報の初期化
    LDibInfo^.bmiHeader.biWidth         := 12;      //  幅
    LDibInfo^.bmiHeader.biHeight        := -102;    //  高さ(向き)

で、本体のデータ作成の部分です。FillMemory の使い方と、普通の配列をポインタで宣言した配列(?)にコピーする方法がよくわかりませんが、エラーが出ないようにしてみました。あってる自信がない自信があります。

    //  ■ 2 ■ データ作成

    //  GetGlyphOutlineの作るデータをまねしてみる
    //  02 02 02 02 02 02 02 02 02 02 02 00 (1回)  0
    //  04 04 04 04 04 04 04 04 04 04 04 00 (13回) 1
    //  00 00 00 00 00 00 00 00 00 00 00 00 (17回) 14
    //  04 04 04 04 04 04 04 04 04 04 04 00 (71回) 31
    FillChar(LBuf, SizeOf(LBuf), $00);
    FillMemory(@(LBuf[0]), 11, $02);
    for i := 1 to 13 do FillMemory(@(LBuf[12 * i]), 11, $04);
    for i := 31 to 101 do FillMemory(@(LBuf[12 * i]), 11, $04);

    //  作ったデータをビットマップの中身にコピー
    Move(LBuf[0], LBitsPtr^, SizeOf(LBuf));

ついでに、SetDIBitsToDevice に渡す高さを指定するときにAbs()を使うことにしました。もともと、LDibInfo^.bmiHeader.biWidthがプラスかマイナスかで描画の向き(マイナスだったら上から下、プラスなら下から上)が変えられるみたいですが、今はマイナスを指定しています。この数字をそのまま高さとして使うと、思ったとおりにかけません。だってマイナスだから。なので、最初のコードは、さらにマイナスしていましたが、ついでなのでどっちでもいけるように絶対値を取ることにします。

さらについで。「走査開始行」と「行数」は、何回かに分割して描画するときに使うようです。ここでは、一撃描画しているので、全体を一度に指定しています。

    //  フォームに描画
    SetDIBitsToDevice(Self.Canvas.Handle,
            90,                                 //  描画する横位置
            90,                                 //  描画する縦位置
            LDibInfo^.bmiHeader.biWidth,        //  幅
            Abs(LDibInfo^.bmiHeader.biHeight),  //  高さ
            0,                                  //  SrcX
            0,                                  //  SrcY
            0,                                  //  走査開始行
            Abs(LDibInfo^.bmiHeader.biHeight),  //  行数
            LBitsPtr,                           //  ビットマップの内容
            LDibInfo^,                          //  TBitmapInfo
            DIB_RGB_COLORS);                    //  CreateDIBSection と同じもの

すると、こうなりました。

しかし変です。

まず「i」は描けてます。もちろんこれはOKです。

それに余分な右端1ピクセルはどうとでもなります。パッと思いつくのは、今はFormに描画していますが、TBitmapに描いてWidthプロパティをいじって1ピクセル縮めるとか(できると思うけど)。描画するときの幅を1ピクセル縮めるとか? 後者の方がよさそうです。

話を戻して、変というのは、ボタンを押すたびに色が変わることです。もう少し正確には、違う画面をアクティブにしてみるなど、何らかの描画処理が走った後でボタンを押すと色が変わるようです。何で?

テキストどおりにやったときはこんなことになりません。

ま。パレットがどうとか何だと思います。その前にやることがある。アンチエイリアスの効いたややこしい漢字をぜひ描画してみたい。でも、またもやネットワークリソースの無駄遣いをしてしまったのでこのページは終わります。

何やかやで楽しくなってきました。

EOFTOP