注:私の元の答えに加えて、賞金が提供された後にこれを追加する
手始めに、300x300ビットマップのみをレンダリングしているため、400x400の背景の長方形は必要ありません。したがって、最初の変更は次のとおりです。
ctx.DrawRectangle(Brushes.White, new Pen(Brushes.White, 10), new System.Windows.Rect(0, 0, 300, 300));
この変更を行うと、出力はまったく同じになりますが、説明が簡単になります。
可能かつ論理的な場合、WPFはDIP(デバイスに依存しないピクセル)をピクセルではなく測定単位として使用します。これを行うとき:
<Rectangle Width="100" Height="100"/>
必ずしもRectangle
100x100の物理ピクセルになるとは限りません。デバイスの物理ピクセル数が96ピクセルより多い(または少ない)場合は、物理ピクセル数が異なります。1インチあたり96ピクセルは、業界標準のようなものだと思います。スマートフォンやタブレットなどの最新のデバイスは、物理的なインチあたりのピクセル数がはるかに多くなっています。WPFが測定単位として物理ピクセルを使用した場合、前述Rectangle
のデバイスでは前述のサイズが小さくなります。
ここで、ビットマップ(または、JPEG、PNG、GIFなど)をレンダリングするには、デバイスに依存するピクセルを使用する必要があります。これは、ラスター形式(ベクトル形式ではない)であるためです。RenderTargetBitmap
そして、コンストラクターを呼び出すときに指定しているのはそれです。結果のビットマップをDPI40の300x300物理ピクセルにすることを伝えています。ソースのDPIは96(モニターが業界標準であると想定)であり、ターゲットのDPIは40であるため、縮小する必要があります。ターゲットに合うソース。したがって、効果はレンダリングされたビットマップ内の縮小された画像です。
ここで本当にやりたいのは、ソースDPIとターゲットDPIが一致していることを確認することです。96をハードコーディングするほど単純ではありません。これは、説明したように、これは単なる標準であり、ソースは実際にはそれよりも多かれ少なかれDPIを持っている可能性があるためです。残念ながら、WPFはDPIを取得するための優れた方法を提供していません。これは、私の意見ではばかげています。ただし、それを取得するために少しp/invokeを実行できます。
public int Dpi
{
get
{
if (this.dpi == 0)
{
var desktopHwnd = new HandleRef(null, IntPtr.Zero);
var desktopDC = new HandleRef(null, SafeNativeMethods.GetDC(desktopHwnd));
this.dpi = SafeNativeMethods.GetDeviceCaps(desktopDC, 88 /*LOGPIXELSX*/);
if (SafeNativeMethods.ReleaseDC(desktopHwnd, desktopDC) != 1 /* OK */)
{
// log error
}
}
return this.dpi;
}
}
private static class SafeNativeMethods
{
[DllImport("User32.dll")]
public static extern IntPtr GetDC(HandleRef hWnd);
[DllImport("User32.dll")]
public static extern int ReleaseDC(HandleRef hWnd, HandleRef hDC);
[DllImport("GDI32.dll")]
public static extern int GetDeviceCaps(HandleRef hDC, int nIndex);
}
これで、関連するコード行を次のように変更できます。
RenderTargetBitmap bity = new RenderTargetBitmap(300, 300, this.Dpi, this.Dpi, PixelFormats.Default);
また、実行しているデバイスに関係なく機能します。最終的には300x300物理ピクセルのビットマップになり、ソースは常にそれを正確に埋めます。