アンチエイリアス処理をかける
文字を描画するときは、アンチエイリアス処理をかけた方が見栄えはよくなります。ただ、モノクロビットマップではアンチエイリアス処理の結果を反映できません。そこで、「sample1.c」を変更して、256色ビットマップファイルを出力する「sample2.c」を作成します。変更部分は太字にしています。
#include<stdio.h> #include<windows.h> #include<stdlib.h> #include<string.h> //BITMAPINFO構造体を再定義 //256色ビットマップなので、RGBQUAD構造体配列の長さは256 typedef struct tagMYBITMAPINFO{ BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[256]; }MYBITMAPINFO; //RGBQUAD構造体配列 int Colors[256] = { 0x00000000,0x00800000,0x00008000,0x00808000, 0x00000080,0x00800080,0x00008080,0x00C0C0C0, 0x00C0DCC0,0x00A6CAF0,0x00402000,0x00602000, 0x00802000,0x00A02000,0x00C02000,0x00E02000, 0x00004000,0x00204000,0x00404000,0x00604000, 0x00804000,0x00A04000,0x00C04000,0x00E04000, 0x00006000,0x00206000,0x00406000,0x00606000, 0x00806000,0x00A06000,0x00C06000,0x00E06000, 0x00008000,0x00208000,0x00408000,0x00608000, 0x00808000,0x00A08000,0x00C08000,0x00E08000, 0x0000A000,0x0020A000,0x0040A000,0x0060A000, 0x0080A000,0x00A0A000,0x00C0A000,0x00E0A000, 0x0000C000,0x0020C000,0x0040C000,0x0060C000, 0x0080C000,0x00A0C000,0x00C0C000,0x00E0C000, 0x0000E000,0x0020E000,0x0040E000,0x0060E000, 0x0080E000,0x00A0E000,0x00C0E000,0x00E0E000, 0x00000040,0x00200040,0x00400040,0x00600040, 0x00800040,0x00A00040,0x00C00040,0x00E00040, 0x00002040,0x00202040,0x00402040,0x00602040, 0x00802040,0x00A02040,0x00C02040,0x00E02040, 0x00004040,0x00204040,0x00404040,0x00604040, 0x00804040,0x00A04040,0x00C04040,0x00E04040, 0x00006040,0x00206040,0x00406040,0x00606040, 0x00806040,0x00A06040,0x00C06040,0x00E06040, 0x00008040,0x00208040,0x00408040,0x00608040, 0x00808040,0x00A08040,0x00C08040,0x00E08040, 0x0000A040,0x0020A040,0x0040A040,0x0060A040, 0x0080A040,0x00A0A040,0x00C0A040,0x00E0A040, 0x0000C040,0x0020C040,0x0040C040,0x0060C040, 0x0080C040,0x00A0C040,0x00C0C040,0x00E0C040, 0x0000E040,0x0020E040,0x0040E040,0x0060E040, 0x0080E040,0x00A0E040,0x00C0E040,0x00E0E040, 0x00000080,0x00200080,0x00400080,0x00600080, 0x00800080,0x00A00080,0x00C00080,0x00E00080, 0x00002080,0x00202080,0x00402080,0x00602080, 0x00802080,0x00A02080,0x00C02080,0x00E02080, 0x00004080,0x00204080,0x00404080,0x00604080, 0x00804080,0x00A04080,0x00C04080,0x00E04080, 0x00006080,0x00206080,0x00406080,0x00606080, 0x00806080,0x00A06080,0x00C06080,0x00E06080, 0x00008080,0x00208080,0x00408080,0x00608080, 0x00808080,0x00A08080,0x00C08080,0x00E08080, 0x0000A080,0x0020A080,0x0040A080,0x0060A080, 0x0080A080,0x00A0A080,0x00C0A080,0x00E0A080, 0x0000C080,0x0020C080,0x0040C080,0x0060C080, 0x0080C080,0x00A0C080,0x00C0C080,0x00E0C080, 0x0000E080,0x0020E080,0x0040E080,0x0060E080, 0x0080E080,0x00A0E080,0x00C0E080,0x00E0E080, 0x000000C0,0x002000C0,0x004000C0,0x006000C0, 0x008000C0,0x00A000C0,0x00C000C0,0x00E000C0, 0x000020C0,0x002020C0,0x004020C0,0x006020C0, 0x008020C0,0x00A020C0,0x00C020C0,0x00E020C0, 0x000040C0,0x002040C0,0x004040C0,0x006040C0, 0x008040C0,0x00A040C0,0x00C040C0,0x00E040C0, 0x000060C0,0x002060C0,0x004060C0,0x006060C0, 0x008060C0,0x00A060C0,0x00C060C0,0x00E060C0, 0x000080C0,0x002080C0,0x004080C0,0x006080C0, 0x008080C0,0x00A080C0,0x00C080C0,0x00E080C0, 0x0000A0C0,0x0020A0C0,0x0040A0C0,0x0060A0C0, 0x0080A0C0,0x00A0A0C0,0x00C0A0C0,0x00E0A0C0, 0x0000C0C0,0x0020C0C0,0x0040C0C0,0x0060C0C0, 0x0080C0C0,0x00A0C0C0,0x00FFFBF0,0x00A0A0A4, 0x00808080,0x00FF0000,0x0000FF00,0x00FFFF00, 0x000000FF,0x00FF00FF,0x0000FFFF,0x00FFFFFF, }; //画像データのサイズを取得する関数 //4バイト(32ビット)区切りになるようにする //4バイト(32ビット)のブロックがいくつになるか計算して、 //4と高さを掛ける int GetSizeImage(int FontSize){ return ((FontSize - 1) / 4 + 1) * 4 * FontSize; } //フォント作成用関数 HFONT CreateMyFont(unsigned char *FontName,int FontSize){ LOGFONT lf; ZeroMemory(&lf,sizeof(LOGFONT)); lf.lfHeight = FontSize; lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = 0; lf.lfItalic = 0; lf.lfUnderline = 0; lf.lfStrikeOut = 0; lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; //ANTIALIASED_QUALITYに設定する lf.lfQuality = ANTIALIASED_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; lstrcpy(lf.lfFaceName,FontName); return CreateFontIndirect(&lf); } //文字描画関数 //Unicode(UTF-16)をいったん文字列に変換してから //TextOutWで文字を出力する void DrawChar(HDC hdc,int CharCode,int FontSize){ wchar_t ws[3]; SIZE size; if(CharCode < 0x10000){ //サロゲートペアでない場合 ws[0] = CharCode; ws[1] = '\0'; } else{ //サロゲートペアだった場合 ws[0] = 0xD800 + (CharCode - 0x10000) / 0x400; ws[1] = 0xDC00 + (CharCode - 0x10000) % 0x400; ws[2] = '\0'; } //半角文字を考慮して中央に描画する GetTextExtentPoint32W(hdc, ws, lstrlenW(ws),&size); TextOutW(hdc,(FontSize - size.cx) / 2,0,ws,lstrlenW(ws)); } //BITMAPFILEHEADER構造体をセットする関数 void SetBitmapFileHeader(BITMAPFILEHEADER *lpbmpfh,int SizeImage){ int OffBits;//画像データまでのオフセット OffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(Colors); ZeroMemory(lpbmpfh,sizeof(BITMAPFILEHEADER)); lpbmpfh->bfType = 0x4D42; //BM lpbmpfh->bfSize = OffBits + SizeImage; lpbmpfh->bfOffBits = OffBits; } //BITMAPINFOHEADER構造体をセットする関数 void SetBitmapInfoHeader(BITMAPINFOHEADER *lpbmpih, int SizeImage, int FontSize){ ZeroMemory(lpbmpih,sizeof(BITMAPINFOHEADER)); lpbmpih->biSize = 40; lpbmpih->biWidth = FontSize; //画像の幅・高さはFontSizeにする lpbmpih->biHeight = FontSize; lpbmpih->biPlanes = 1; lpbmpih->biBitCount = 8; //256色ビットマップの場合は8 lpbmpih->biSizeImage = SizeImage; } : :(中略) : //画像ファイルを変更 int main(void){ CharCodeToBitmapFile(0x3042,"MS 明朝",100,".\\004.bmp");//あ CharCodeToBitmapFile(0x4E9C,"MS ゴシック",200,".\\005.bmp");//亜 CharCodeToBitmapFile(0x20B9F,"MS 明朝",300,".\\006.bmp"); //叱(サロゲートペア) return 0; }
まとめ
以上で、解説を終わりますが、今回はデバイスコンテキストにGDI関数で描画してその内容をビットマップファイルへ保存しました。ただ、ほかにも同様の機能を実現する方法はいくつかあります。
WindowsAPIのGetGlyphOutline
を使えば、文字の情報にダイレクトにアクセスできます。また、GDやImageMagickのような画像処理系のライブラリを使って、画像を動的に作成する方法もあります。詳しくは参考資料のURLを参照してください。
参考資料
- 近藤正芳のウェブページ『BMP ファイルフォーマット』
- うめっきぃ『Bitmapファイルフォーマット』
- Windowsプログラミング研究所『DDB→DIB変換』
- Wikipedia『アンチエイリアス』
- MSDN『LOGFONT』
- EternalWindows『アンチエイリアシング』
- 68user's page『画像生成』
- Cepheid『ImageMagickの使用例 - 画像へ文字を記入する』