ようこそゲストさん

つーさのくーかん - 西条は完全に冬 -

つーさによるXG自作曲・C#ゲーム他プログラミング・日々あれこれ趣味私用雑多ブログ型ウェブサイト : Ver 7.011.0

2008/02/02(Sat) アンチエイリアス文字描画モジュール

はてブ情報 はてブに登録 はてブ数 2008/02/02 15:00 プログラミング::HSP3

久しぶりに若干バージョンアップしたのでage.
2008. 2. 2 / ver 0.20 縦に描画位置がズレる問題修正と描画高速化。
2008. 5.14 / ver 0.21 描画クリップ枠が上に1pxズレていて落ちてしまう問題の修正。

mes命令に比較するとかなり重い(APIが重いから)ですが、
フォントからグリフを得て描画することにより、
綺麗で目に優しい文字を描画することができます。

このモジュールはスクリプトに組み込んで自由に使用できます。

/* ames.hsp */
// アンチエイリアス文字描画モジュール / 月影とも 2005. 9.11
// 2008. 2. 2 / ver 0.20 縦に描画位置がズレる問題修正。マシン語高速化。
// 2008. 5.14 / ver 0.21 描画クリップ枠が上に1pxズレていて落ちてしまう問題の修正。
#ifndef xdim
#uselib "kernel32.dll"
#func global VirtualProtect@_xdim "VirtualProtect" var,int,int,var
#define global xdim(%1,%2) dim %1,%2: VirtualProtect@_xdim %1,%2*4,$40,x@_xdim
#endif

#module "mod_ames"
;// 追加命令
; ames str "string" // メッセージを表示
; text int delay // hsp3util 互換のテキストディレイモード。
; ames_cancel // ディレイキャンセル。 onclick gosub 内などで使用可能。

#define ctype IsZenkaku(%1) ((((%1)^$20)-$a1&$ff)<=$3b) ;// Shift-Jis用 (日本語Windows)
;#define ctype IsZenkaku(%1) ((%1)<128) ;// 他言語(無保証)……

#uselib "gdi32.dll"
#func GetTextMetrics "GetTextMetricsA" int,int
#func GetGlyphOutline "GetGlyphOutlineA" int,int,int,var,int,int,var
#deffunc _init_ames_
;int WINAPI DrawGlyph(BMSCR *pBmscr, LPTEXTMETRIC lpTextMetric, LPGLYPHMETRICS lpGlyphMetrics, LPBYTE pbGryph, DWORD cbGryph)
xdim f,111
f.$00=$8b28ec83,$53342454,$55085a8b,$57562a8b,$3c247c8b,$8b14478b,$5f03084f,$2444896c,$04478b28,$0340748d,$24244489,$2b70478b,$4c890c42,$4a8b1024,$24548b04,$04420340
f.$10=$1c246c89,$8303c583,$e583fce6,$89c985fc,$892c2474,$8930246c,$0f34245c,$0001448e,$24548b00,$24448948,$89d8f740,$89482454,$893c2444,$eb20244c,$24a48d07,$00000000
f.$20=$4024448b,$1024443b,$00fc8d0f,$c0850000,$00f48c0f,$d2330000,$1c245439,$14245489,$00e48e0f,$5c890000,$2c8d1824,$00498d5b,$4824448b,$100cb60f,$840fc985,$000000a3
f.$30=$24245c3b,$00998d0f,$db850000,$00918c0f,$448b0000,$5c8b3c24,$448d1024,$af0fff18,$03c503c6,$83282444,$5a7440f9,$a297b60f,$0f000000,$af0f18b6,$0040bed1,$f12b0000
f.$40=$03deaf0f,$58b60fd3,$06fac101,$88deaf0f,$97b60f10,$000000a1,$03d1af0f,$06fac1d3,$0f015088,$00a097b6,$af0f0000,$48b60fd1,$ceaf0f02,$2c24748b,$fac1d103,$02508806
f.$50=$1424548b,$b60f1deb,$0000a28f,$0f088800,$00a18fb6,$48880000,$8fb60f01,$000000a0,$8b024888,$8318245c,$c38301c2,$03c58301,$1c24543b,$14245489,$18245c89,$ff2e8c0f
f.$60=$6c8bffff,$5c8b3024,$6c013424,$01b84824,$29000000,$013c2444,$29402444,$0f202444,$fffedb85,$24548bff,$42bf0f44,$6c470110,$335d5e5f,$c4835bc0,$0014c228


pfnDrawGlyph = varptr(f) : return

#deffunc ames str _string
    _str = _string + "\n"
    mref bmscr, 67
    pos_startx = ginfo_cx
    redrawSw = wpeek(bmscr, 78)
    redraw 0
    dim vGlyphmetrics,5 : vMat2=$10000,0,0,$10000
    dim vTextMetric,7 : GetTextMetrics hdc, varptr(vTextMetric)
    delay = stwait@hsp3util

    repeat strlen(_str)
        code = peek(_str, cnt)
        if IsZenkaku(code) { code = code<<8 | peek(_str, cnt+1) }
        if code=$0D {
            bmscr.68 = ginfo_cx - pos_startx, bmscr.32
            pos pos_startx, ginfo_cy+bmscr.32
            continue cnt+2
        }
        GetGlyphOutline hdc, code, 6, vGlyphmetrics, 0, 0, vMat2 : _sz = stat
        sdim bmpbuffer, _sz
        GetGlyphOutline hdc, code, 6, vGlyphmetrics, _sz, varptr(bmpbuffer) , vMat2
        prm = varptr(bmscr), varptr(vTextMetric), varptr(vGlyphmetrics), varptr(bmpbuffer), _sz
        ret = callfunc(prm,pfnDrawGlyph,5)
        if delay : redraw redrawSw : await delay : redraw 0
        if code>256 : continue cnt+2
    loop
    redraw redrawSw
return 

#deffunc drawglyph int char
    mref bmscr, 67
    dim vGlyphmetrics,5 : vMat2=$10000,0,0,$10000
    dim vTextMetric,7 : GetTextMetrics hdc, varptr(vTextMetric)
    GetGlyphOutline hdc, char, 6, vGlyphmetrics, 0, 0, vMat2 : _sz = stat
    sdim bmpbuffer, _sz
    GetGlyphOutline hdc, char, 6, vGlyphmetrics, _sz, varptr(bmpbuffer) , vMat2
    prm = varptr(bmscr), varptr(vTextMetric), varptr(vGlyphmetrics), varptr(bmpbuffer), _sz
    ret = callfunc(prm,pfnDrawGlyph,5)
    redraw wpeek(bmscr, 78)
return

#ifndef text@
#define global text(%1) stwait@hsp3util=%1
#endif
#define global ames_cancel delay@mod_ames = 0

#global
_init_ames_
// 各種さんぷる
#include "ames.hsp"

// アンチエイリアス文字描画 
    font "MS 明朝",40 // 必ずフォントを指定してください。
    redraw 0
    color 255,0,0 : mes  "アンチエイリアス文字描画"
    color 0,0,255 : ames "アンチエイリアス文字描画"
    color 255,0,0 : pos 0,80 : mes  "アンチ   アス文字  "
    color 0,0,255 : pos 0,80 : ames "   エイリ    描画"
    redraw 1

// 500回描いて速度測定。
// 結局のところ GetGlyphOutline が遅いので……
    font "MS P明朝",28
    #uselib "winmm.dll"
    #cfunc timeGetTime "timeGetTime"
    title "描画速度計測中..."

    redraw 0
    st = timegettime()
    repeat 500
        pos 0,160
        ames "文字の描画速度計測♪" 
    loop
    gt = timegettime()
    redraw 1
    mes ""
    title "描画速度計測中... 完了 : "+(gt-st)+"ms."

// ディレイキャンセルのサンプル。
    font "MS P明朝",28
    text 150 // ディレイを設定。
    onclick gosub *c
    ames {"クリックするとディレイキャンセルします。
    画面をクリックしてみてください。
    残りの文字を一気に表示することができます。

    ちなみに、このパソコンは"}+ ((gt-st)/5 )+{"マイクロ秒くらいで
    28pxの文字を1文字、描画できるみたいです。"}

    stop
 *c 
    ames_cancel
    return
/* ames.c */
#include <windows.h>
#include "j:/program files/hsp30/hspsdk/sample/hsp3plugin.h"

int WINAPI DrawGlyph(BMSCR *pBmscr, LPTEXTMETRIC lpTextMetric, LPGLYPHMETRICS lpGlyphMetrics, LPBYTE pbGryph, DWORD cbGryph)
{
    LPBYTE pBitmap = pBmscr->pBit;
    LPBYTE pColor = (LPBYTE)&pBmscr->color;

    int sx = pBmscr->sx;
    int sxa = sx*3+3&~3;
    int sy = pBmscr->sy;

    int lx = lpGlyphMetrics->gmBlackBoxX;
    int lxa = lx+3&~3;
    int ly = lpGlyphMetrics->gmBlackBoxY;

    int ox = pBmscr->cx + lpGlyphMetrics->gmptGlyphOrigin.x;
    int oy = pBmscr->cy - lpGlyphMetrics->gmptGlyphOrigin.y + lpTextMetric->tmAscent;

    for(int y=0; y<ly; y++)
    {
        int py = oy+y;
        if(py >= sy || py < 0) continue;

        for(int x=0; x<lx; x++)
        {
            int blend = pbGryph[y*lxa+x];
            if(blend == 0) continue;

            int px = ox+x;
            if(px >= sx || px < 0) continue;

            LPBYTE pPx = pBitmap + (sy-py-1)*sxa + px*3;
            if(blend != 64)
            {
                int invblend = 64-blend;
                pPx[0] = (pColor[2]*blend + pPx[0]*invblend);
                pPx[1] = (pColor[1]*blend + pPx[1]*invblend);
                pPx[2] = (pColor[0]*blend + pPx[2]*invblend);
            }
            else
            {
                pPx[0] = pColor[2];
                pPx[1] = pColor[1];
                pPx[2] = pColor[0];
            }

        }
    }

    pBmscr->cx += lpGlyphMetrics->gmCellIncX;
    return 0;
}

1: クゥ 2008年01月30日(Wed) 午後5時51分

こんにちは。HSP3.1で、最初の四角の部分をコピペして
テストを行ってみましたが動作しませんでした。
HSPのバージョンにあっていないのでしょうか。
是非使わせて頂きたいモジュールなので、解決策があるようでしたら、
ご回答宜しくお願いします。
それと、#uselib "winmm"の部分に.dllが抜けていました。だいぶ前に作られたモジュールへの質問なので
お手数をかけますが、お願いします。

2: つーさ 2008年01月31日(Thr) 午前6時55分

サンプルを見るには、#if 1 としてください。
.dllは省略しても動くのですが、気持ち悪かったら付けておいてください。

3: クゥ 2008年02月01日(Fri) 午後0時05分

お忙しい中ご回答どうもありがとうございます、無事使うことが出来ました。
自作ゲームの文字表示に使わせていただきます。
本当にありがとうございました、それでは失礼します。

4: 現矢 2008年03月02日(Sun) 午後0時32分

いつもこちらにお世話になっております、ありがとうございます。
今回、こちらのアンチエイリアス絵画モジュールを使用させていただいたのですが、不思議なことがありましたのでご報告いたします。
amesの文字列が半角英数の場合は起こらないのですが、全角文字が入っているときに、フォントサイズを9以下にするとシステムエラーが起こります。

font "MS 明朝",9
ames "AAAあ"

しかし、先に10以上の数をフォントに指定して絵画してからならば、エラーは起こりません。

font "MS 明朝",10
ames "AAAあ"
font "MS 明朝",9
ames "AAAあ"

プログラムが始まって最初に9以下で絵画するということもあまり無いとは思うのですが、念のためご報告申し上げます。
つーさ様には、いつも勝手ながら助けられております、本当に感謝の念に耐えない日々です。ありがとうございます。これからも、どんな鮮やかなプログラムを書かれるのか楽しみにお待ちしています。

5: つーさ 2008年03月02日(Sun) 午後3時55分

おお、これは奇妙な現象……。
いったん、10px以上で描くとエラーが起こらないってのは、メモリ確保周りな気がしますが、追って原因を調べてみることにします。
ご報告ありがとうございましたー。

6: なたで 2008年03月31日(Mon) 午前10時40分

つーささん初めまして、なたでです。
つーささんの高度なコードで、
以前から参考にさせてもらっています。ヾ(´▽`*)ゝ

ames.c の34行目の計算で縦のサイズが1つずれているのが原因でした。
LPBYTE pPx = pBitmap + (sy-py-1)*sxa + px*3;
とすることで、データの外に書き込まなくなり正常に動作するようになります。

font "MS 明朝",10
ames "AAAあ"
font "MS 明朝",9
ames "AAAあ"

これでエラーが起きなかったのは、
2回目のamesで書き込む位置のカレントポジションが移動しているからです。
フォントサイズが10pxより大きいとエラーが出なかったのは、
偶然その外の領域に書き込まなかったからでした。

これからも、音楽活動・プログラム頑張ってください!
応援してます(〃⌒∇⌒)ゞえへへっ♪

7: つーさ 2008年04月01日(Tue) 午前5時08分

コメントありがとうございます。
しかし、高度なコードとは(笑)
いや、自分なんてまだまだ若輩者です。

バグの原因はまさにご指摘の通りです(とか言って
本当は前のコメントの後ですぐに気づいたのですが、
他のやることとやりたいことに追われて怠慢しておりました^^;
修正までしないにせよ、情報くらいは載せるべきだったかなと反省しております。

ご指摘、励まし、ありがとうございました。
これからもよろしくお願いします。

8: あう 2008年05月14日(Wed) 午後3時32分

こんにちわ。モジュール利用させていただいています。バグ(?)を見つけたのでご報告を。サンプルの部分から一部コピペして以下のようにしました。

#include "ames.hsp"

font "MS P明朝",28
text 150 // ディレイを設定。
onclick gosub *c
ames {"クリックするとディレイキャンセルします。
画面をクリックしてみてください。
残りの文字を一気に表示することができます。

ちなみに、このパソコンは"}+ ((gt-st)/5 )+{"マイクロ秒くらいで
28pxの文字を1文字、描画できるみたいです。"}

stop
*c
ames_cancel
return

ディレイ部分だけでテストしてみると、エラーが出ます。
pos 1,1 以上を書き足すとエラーは出ないようです。

9: つーさ 2008年05月14日(Wed) 午後10時33分

詳細な情報ありがとうございます。
最初に当該問題をご報告いただいてからから
約2ヶ月半近く放置してしまったことになりますが、
本日ようやく修正しました(^^;
これでマイナス領域に描いても大丈夫、なハズ。

10: ととん 2008年07月30日(Wed) 午前1時51分

あるフリーフォントを使用すると、一部の文字で文字化けが発生しました。
で、原因を探ってみると、ames.c のある部分を下のように変更すれば、問題なく表示されました。

int lxa = lx+3&~3;
int ly = lpGlyphMetrics->gmBlackBoxY;
 ↓
int ly = lpGlyphMetrics->gmBlackBoxY;
int lxa = cbGryph/ly;

他のフォントでも文字化けするのかな?ひょっとしたら、特殊なフォントだったのかもしれません~。
ではでは、つーささんのサイトを重宝させて頂いている者からでした。
m(__)m

11: つーさ 2008年07月30日(Wed) 午前9時42分

これは初めてのケースかも。フリーのフォントということもあるので、
おそらく、フォント情報が正確に入力されていないフォント?なのではないかと思っていますが、
このAPI自体もやたらとバグが多いので何とも言えないところも……。
こういう場合においてcbGryphをlyで割るのはなかなかウマい方法ですね :-)
コメントありがとうございました。


#  非公開コメント