C#
Metafileの解像度
Metafileの作成で難しいところがあったのでメモしておくことにした。Metafileを作ると、画像としてのサイズは指定した通りになるのだが、中の描画の縮尺がおかしい。さっぱり理由がわからなかった。
[DllImport("Gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("Gdi32.dll")]
static extern int DeleteDC(IntPtr hdc);
IntPtr hDC = CreateCompatibleDC(IntPtr.Zero);
try
{
Rectangle FrameRect = new Rectangle(0, 0, 216, 216);
using (Metafile _Metafile = new Metafile("sample.emf", hDC, FrameRect, MetafileFrameUnit.Point, EmfType.EmfPlusDual))
{
using (Graphics g = Graphics.FromImage(_Metafile))
{
g.PageUnit = GraphicsUnit.Point;
g.FillRectangle(Brushes.White, FrameRect);
g.FillRectangle(Brushes.Green, 72, 72, 72, 72);
}
}
}
finally
{
DeleteDC(hDC);
}
Metafileのプロパティを見ると、画像としてのサイズは合っているのだが、解像度がおかしいことに気付いた。よくわからないままにScaleTransform()で縮小したら、うまくいった。
g.ScaleTransform(0.846f, 0.846f);
例えばGraphicsの解像度が96.0dpi、Metafileの解像度が81.2dpiだとすると、Graphicsでは1.0インチが96.0ピクセルになり、それがMetafileでは96.0÷81.2≒1.18インチになってしまうのかなと思った。それなら81.2÷96.0≒0.846倍に縮小すれば、拡大されて描画されるときに元の長さに戻ってうまくいくだろう。
ただ、元にするデバイスコンテキストが変わるとMetafileの解像度も変わるし、描画する前にMetafileの解像度を知ることもできない。メモリデバイスコンテキストはディスプレイデバイスと互換性があるので、実行環境によっても解像度が変わるはず。困ったものだ。それにしても、いったいどうしてMetafileの解像度はこんなに変な値なのだろう。いろいろ試したりしてるうちに、デバイスコンテキストの情報を調べて、ようやくわかった。MetafileではHORZSIZE, VERTSIZE, HORZRES, VERTRESから解像度が計算されるらしい。
[DllImport("Gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
{
const int HORZSIZE = 4;
const int VERTSIZE = 6;
const int HORZRES = 8;
const int VERTRES = 10;
int HorzSize_Mm = GetDeviceCaps(hDC, HORZSIZE);
int VertSize_Mm = GetDeviceCaps(hDC, VERTSIZE);
float HorzSize_In = HorzSize_Mm / 25.4f;
float VertSize_In = VertSize_Mm / 25.4f;
int HorzRes = GetDeviceCaps(hDC, HORZRES);
int VertRes = GetDeviceCaps(hDC, VERTRES);
float DpiX = HorzRes / HorzSize_In;
float DpiY = VertRes / VertSize_In;
float ScaleX = DpiX / g.DpiX;
float ScaleY = DpiY / g.DpiY;
g.ScaleTransform(ScaleX, ScaleY);
}
Metafileにこんな正確な解像度なんて必要あるのかな。素直にLOGPIXELSXとLOGPIXELSYを設定してくれても良かったのにと思うが、何か歴史的な背景でもあるのだろうか。
(2018/10/28 初稿)