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;
}