任意解像度のビットマップにミリ単位で描画する方法
ビットマップファイルを動的に生成する場合、解像度情報はデフォルトの96dpiで生成される。
デバイスに依存しないで、任意解像度のビットマップにMM_HIMETRIC(1/100mm)単位で描画する場合、以下の手順で、ビューポート範囲等を設定する。
void SaveImage(HDC hDC, long Width, long Height, int dpi) { ::SaveDC(hDC); HPEN hPen = ::CreatePen(PS_SOLID|PS_GEOMETRIC, 10, RGB(0,0,0)); HBRUSH hBrush = ::CreateSolidBrush(RGB(0xff, 0xff, 0xff)); ::SelectObject(hDC, hPen); RECT rr = {0, 0, Width, Height}; ::FillRect(hDC, &rr, hBrush); const double INCH_TO_MM = 25.4; // 1inch = 25.4mm double logicalWidth = (double)Width / dpi * INCH_TO_MM; double logicalHeight = (double)Height / dpi * INCH_TO_MM; // MM_HIMETRIC相当の論理幅 long lw = (long)(logicalWidth * 100.0 + 0.5); long lh = (long)(logicalHeight * 100.0 + 0.5); SIZE LastSize; SIZE Size; POINT LastPoint; ::SetMapMode(hDC, MM_ANISOTROPIC); // 論理単位を任意に ::SetWindowExtEx(hDC, lw, -lh, &LastSize); // DCの論理幅・方向をmm単位で上方向を正とする。 ::SetViewportExtEx(hDC, Width, Height, &LastSize); // DCの物理幅は設定サイズ // 描画原点をDCの中心とする。原点を左下にしたい場合は、abs(Size.cy / 2)をabs(Size.cy)に ::GetViewportExtEx(hDC, &Size); ::SetViewportOrgEx(hDC, abs(Size.cx / 2), abs(Size.cy / 2), &LastPoint); // 一本線を書いてみる POINT pt; ::MoveToEx(hDC, 0, 0, &pt); ::LineTo(hDC, 5000, -5000); // DCを元に戻す ::RestoreDC(hDC, -1); // 後始末 ::DeleteObject(hPen); ::DeleteObject(hBrush); }
あとは、保存先のTPictureにTCanvas::CopyRectやBitBltなりで転送して、ファイルを保存。 ATL::CImageで生成したオブジェクトのhDCに渡すのもOK。 こしらえたビットマップファイルの解像度情報は96dpi(=画面)になっているので、最後に解像度情報をいじってやる。
// ビットマップファイルの解像度情報を書き換え FILE* fp; fopen_s(&fp, FileName, "r+b"); fseek(fp, 38, SEEK_SET); DWORD32 dpm; dpm = (int)(dpi * 1000.0 / INCH_TO_MM); fwrite((void*)&dpm, sizeof(dpm), 1, fp); fwrite((void*)&dpm, sizeof(dpm), 1, fp); fclose(fp);
追記:
しかし、C++Builderじゃないけど、ATL::CImageはかなり便利。MFCとの混在も当然可能だし、ファイルの保存とかも一発でやってくれる。昔、ファイルに保存するとき、ビットマップヘッダとかパレット情報とか色々生成したな・・・。(遠い目)