大まかな流れ
次のような手順でビットマップファイルを作成します。
- RGBQUAD構造体の配列を宣言……①
- BITMAPFILEHEADER構造体をセット……②
- BITMAPINFOHEADER構造体をセット……③
- CreateCompatibleBitmapでデバイスコンテキストを用意
- TextOut関数で文字を描画
- GetDIBitsで画像データを取得……④
- ②-③-①-④の順番でファイルへ出力
GetDIBits関数
この関数は、デバイスコンテキストからビットマップデータを取得するためのものですが、次のように定義されています。
int GetDIBits( HDC hdc, // デバイスコンテキストのハンドル HBITMAP hbmp, // ビットマップのハンドル UINT uStartScan, // 取得対象の最初の走査行 UINT cScanLines, // 取得対象の走査行の数 LPVOID lpvBits, // ビットマップのビットからなる配列 LPBITMAPINFO lpbi, // ビットマップデータのバッファ UINT uUsage // RGB とパレットインデックスのどちらか );
ポイントは、6番目の引数のlpbi
で、これはBITMAPINFO構造体のポインタです。
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO;
メンバの最後にRGBQUAD構造体の長さ1の配列がありますが、実際はメモリアロケーションなどを使って可変長配列として扱います。つまり、BITMAPINFO構造体とは、BITMAPINFOHEADER構造体の後に色数分のRGBQUAD構造体が続いているものです。
今回は、メモリアロケーションを使わず、BITMAPINFO構造体を次のように再定義して使用します。
typedef struct tagMYBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2]; } MYBITMAPINFO;
モノクロビットマップなので、RGBQUAD構造体の配列の長さを2にしておきます。
サンプルコード
以下のような関数を自作して、それをmain
から呼び出すサンプルコードです。
void CharCodeToBitmapFile(int CharCode, unsigned char *FontName, int FontSize, unsigned char *FileName)
引数 | 内容 |
CharCode | ビットマップファイルに変換したい文字の文字コードをUnicode(UTF-16)で指定します。 |
FontName | フォント名を指定します。 |
FontSize | フォントサイズを指定します。単位はピクセルです。ここで指定した値が、出力される画像の幅と高さになります。 |
FileName | 出力されるビットマップファイルのパスを指定します。 |
#include<stdio.h> #include<windows.h> #include<stdlib.h> #include<string.h> //BITMAPINFO構造体を再定義 //モノラルビットマップなので、RGBQUAD構造体配列の長さは2 typedef struct tagMYBITMAPINFO{ BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2]; }MYBITMAPINFO; //RGBQUAD構造体配列 int Colors[2] = { 0x00000000, //黒 0x00FFFFFF, //白 }; //画像データのサイズを取得する関数 //4バイト(32ビット)区切りになるようにする //4バイト(32ビット)のブロックがいくつになるか計算して、 //4と高さを掛ける int GetSizeImage(int FontSize){ return ((FontSize - 1) / 32 + 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; lf.lfQuality = DEFAULT_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 = 1; //モノクロビットマップの場合は1 lpbmpih->biSizeImage = SizeImage; } //lpvBitsへ画像データを読み込む関数 void SetBitmapData(void *lpvBits, BITMAPINFOHEADER *lpbmpih, int CharCode,unsigned char *FontName, int FontSize){ HDC hdcScreen,hdc; HBITMAP hBitmap,hBitmapOld; HFONT hFont,hFontOld; RECT rect; MYBITMAPINFO mybmpi; hdcScreen = GetDC(NULL); //スクリーンのデバイスコンテキストを取得 hdc = CreateCompatibleDC(hdcScreen); //スクリーン互換の //デバイスコンテキストを取得 hBitmap = CreateCompatibleBitmap(hdcScreen,FontSize,FontSize); //スクリーン互換のビットマップを作成 hBitmapOld = SelectObject(hdc,hBitmap); hFont = CreateMyFont(FontName,FontSize);//フォントを作成 hFontOld = SelectObject(hdc,hFont); //半角文字を考慮して、背景を白で埋めておく SetRect(&rect,0,0,FontSize,FontSize); FillRect(hdc,&rect,GetStockObject(WHITE_BRUSH)); DrawChar(hdc,CharCode,FontSize);//文字を描画 //MYBITMAPINFO構造体をセット mybmpi.bmiHeader = *lpbmpih; memcpy(mybmpi.bmiColors,Colors,sizeof(Colors)); //lpvBitsへビットマップデータを読み込む GetDIBits(hdc,hBitmap,0,FontSize, lpvBits,(BITMAPINFO *)&mybmpi,DIB_RGB_COLORS); //以下、終了処理 DeleteObject(SelectObject(hdc,hBitmapOld)); DeleteObject(SelectObject(hdc,hFontOld)); DeleteDC(hdc); ReleaseDC(NULL,hdcScreen); } void CharCodeToBitmapFile(int CharCode, unsigned char *FontName, int FontSize, unsigned char *FileName){ int SizeImage; BITMAPFILEHEADER bmpfh; BITMAPINFOHEADER bmpih; void *lpvBits; FILE *fp; //画像データのサイズを取得 SizeImage = GetSizeImage(FontSize); //BITMAPFILEHEADER構造体をセット SetBitmapFileHeader(&bmpfh,SizeImage); //BITMAPINFOHEADER構造体をセット SetBitmapInfoHeader(&bmpih,SizeImage,FontSize); //バッファを用意 lpvBits = (void *)malloc(SizeImage); //画像データを読み込む SetBitmapData(lpvBits,&bmpih,CharCode,FontName,FontSize); //ファイルへの書き込み fp = fopen(FileName,"wb"); fwrite(&bmpfh,sizeof(BITMAPFILEHEADER),1,fp); fwrite(&bmpih,sizeof(BITMAPINFOHEADER),1,fp); fwrite(Colors,sizeof(Colors),1,fp); fwrite(lpvBits,SizeImage,1,fp); fclose(fp); free(lpvBits);//バッファを解放 } int main(void){ CharCodeToBitmapFile(0x3042,"MS 明朝",100,".\\001.bmp");//あ CharCodeToBitmapFile(0x4E9C,"MS ゴシック",200,".\\002.bmp");//亜 CharCodeToBitmapFile(0x20B9F,"MS 明朝",300,".\\003.bmp"); //叱(サロゲートペア) return 0; }