SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

文字コードから画像ファイルを生成する

BMP構造とCを使った画像ファイル作成の説明

  • X ポスト
  • このエントリーをはてなブックマークに追加

大まかな流れ

 次のような手順でビットマップファイルを作成します。

  1. RGBQUAD構造体の配列を宣言……①
  2. BITMAPFILEHEADER構造体をセット……②
  3. BITMAPINFOHEADER構造体をセット……③
  4. CreateCompatibleBitmapでデバイスコンテキストを用意
  5. TextOut関数で文字を描画
  6. GetDIBitsで画像データを取得……④
  7. ②-③-①-④の順番でファイルへ出力

GetDIBits関数

 この関数は、デバイスコンテキストからビットマップデータを取得するためのものですが、次のように定義されています。

GetDIBits関数
int GetDIBits(
  HDC hdc,           // デバイスコンテキストのハンドル
  HBITMAP hbmp,      // ビットマップのハンドル
  UINT uStartScan,   // 取得対象の最初の走査行
  UINT cScanLines,   // 取得対象の走査行の数
  LPVOID lpvBits,    // ビットマップのビットからなる配列
  LPBITMAPINFO lpbi, // ビットマップデータのバッファ
  UINT uUsage        // RGB とパレットインデックスのどちらか
);

 ポイントは、6番目の引数のlpbiで、これはBITMAPINFO構造体のポインタです。

BITMAPINFO構造体
typedef struct tagBITMAPINFO {
   BITMAPINFOHEADER bmiHeader; 
   RGBQUAD          bmiColors[1]; 
} BITMAPINFO; 

 メンバの最後にRGBQUAD構造体の長さ1の配列がありますが、実際はメモリアロケーションなどを使って可変長配列として扱います。つまり、BITMAPINFO構造体とは、BITMAPINFOHEADER構造体の後に色数分のRGBQUAD構造体が続いているものです。

 今回は、メモリアロケーションを使わず、BITMAPINFO構造体を次のように再定義して使用します。

MYBITMAPINFO構造体
typedef struct tagMYBITMAPINFO {
   BITMAPINFOHEADER bmiHeader; 
   RGBQUAD          bmiColors[2]; 
} MYBITMAPINFO; 

 モノクロビットマップなので、RGBQUAD構造体の配列の長さを2にしておきます。

サンプルコード

 以下のような関数を自作して、それをmainから呼び出すサンプルコードです。

CharCodeToBitmapFile関数
void CharCodeToBitmapFile(int CharCode,
                          unsigned char *FontName,
                          int FontSize,
                          unsigned char *FileName)
引数の説明
引数内容
CharCodeビットマップファイルに変換したい文字の文字コードをUnicode(UTF-16)で指定します。
FontNameフォント名を指定します。
FontSizeフォントサイズを指定します。単位はピクセルです。ここで指定した値が、出力される画像の幅と高さになります。
FileName出力されるビットマップファイルのパスを指定します。
sample1.c
#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;
}

次のページ
アンチエイリアス処理をかける

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

さなみ(サナミ)

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/1643 2008/01/18 23:31

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング