余白を含めたフォントサイズを取得する
文字画像を作成するためにも、まずは画像サイズを決めなければなりません。画像サイズは、余白も含めた文字サイズと同じにします。ここで考慮しなければならないのは、プロポーショナルフォントと等幅フォントの存在です。フォントには大きく分けて、文字によって横幅が異なるプロポーショナルフォントと、すべての文字の横幅が等しい等幅フォントとの2種類が存在します。近年のアスキーアートは某巨大掲示板に合わせてプロポーショナルフォントで作ることもありますが、本来は等幅フォントで作るものであり、またプログラムによる自動生成ではプロポーショナルフォントに対応するのは難しすぎるため、等幅フォント限定ということで話を進めます。
フォントの詳細な情報は、GetTextMetrics
関数に渡すと設定してくれるTEXTMETRIC
構造体を調べれば分かります。LOGFONT
構造体にもサイズを表すメンバが存在しますが、直接的にピクセル数を表しているとは限らないため使えません。
ところでDIBSectionを作る方法ですが、私が作った画像入出力クラスBmpIO
を使えば、BmpIO::CreateEmpty
関数で空のDIBSectionを作ることができます。HDCはBmpIO::GetHDC
関数で、ピクセル列を指すポインタはBmpIO::GetPixel
関数で取得できます。他の公開関数については「BmpIO.txt」を見てください。
以上をまとめると、文字を画像(DIBSection)に変換するためのプログラムは次のようになります。文字の色は黒、背景の色は白にします。これは後述する2値化した画像と比較するためです。
// 文字をビットマップに変換 static void AsciiToBmp(BmpIO *pBmpIO, int c, const LOGFONT *pLF) { if(pBmpIO==NULL || pLF==NULL) return; HDC hdc=CreateDC("DISPLAY",NULL,NULL,NULL); // デバイス作成 HFONT hFont=CreateFontIndirect(pLF); // フォント作成 HFONT hOldFont=(HFONT)SelectObject(hdc,hFont); TEXTMETRIC tm; GetTextMetrics(hdc,&tm); // フォント情報を取得 SelectObject(hdc,hOldFont); DeleteDC(hdc); // デバイス削除 DWORD w=tm.tmAveCharWidth; // 固定幅フォントの半角文字の幅 DWORD h=tm.tmHeight; // 文字の高さ pBmpIO->CreateEmpty(w,h,BIT24); // ビットマップ作成 hdc=pBmpIO->GetHDC(); hOldFont=(HFONT)SelectObject(hdc,hFont); char str[2]; wsprintf(str,"%c",c); TextOut(hdc,0,0,str,1); // 文字をビットマップに描画 SelectObject(hdc,hOldFont); DeleteObject(hFont); }
文字画像と等しいサイズに画像の一部を切り取る
アスキーアートに変換する原画像は文字画像より大きいため、文字画像と等しいサイズの小さな画像に分割しなければなりません。論理的に分割するだけなら原画像の参照範囲を設定するだけですが、より簡単に文字画像と比較するために、原画像の一部を切り取った分割画像を新たに作ります。
プログラムは次のようになります。原画像の範囲外を参照した場合は文字画像の背景色である白に設定します。
// 一部を切り取ったビットマップを作成 static void SplitBmp(BmpIO *pDst, BmpIO *pSrc, int sx, int sy ,int dw, int dh) { if(pDst==NULL || pSrc==NULL) return; // 作成するビットマップ pDst->CreateEmpty(dw,dh,BIT24); DWORD l=pDst->GetLength(); BYTE *p=pDst->GetPixel(); BYTE b=pDst->GetBitCount()/8; // 切り取り元ビットマップ int sw=(int)pSrc->GetWidth(); int sh=(int)pSrc->GetHeight(); DWORD sl=pSrc->GetLength(); BYTE *sp=pSrc->GetPixel(); if(p==NULL || sp==NULL) return; int x,y,xb,yl,sxb,syl; for(y=0;y<dh;y++) { yl=y*l; syl=(sy+y)*sl; for(x=0;x<dw;x++) { xb=x*b; if(sx+x>=0 && sx+x<sw && sy+y>=0 &&sy+y<sh) { sxb=(sx+x)*b; p[xb +yl]=sp[sxb +syl]; p[xb+1+yl]=sp[sxb+1+syl]; p[xb+2+yl]=sp[sxb+2+syl]; } else // 切り取り元ビットマップの範囲外 { p[xb +yl]=255; // 白背景 p[xb+1+yl]=255; p[xb+2+yl]=255; } } } }