此代码生成以下图像。
DrawingVisual visual = new DrawingVisual();
DrawingContext ctx = visual.RenderOpen();
FormattedText txt = new FormattedText("45", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Verdana"), 100, Brushes.Red);
ctx.DrawRectangle(Brushes.White, new Pen(Brushes.White, 10), new System.Windows.Rect(0, 0, 400, 400));
ctx.DrawText(txt, new System.Windows.Point((300 - txt.Width)/2, 10));
ctx.Close();
RenderTargetBitmap bity = new RenderTargetBitmap(300, 300, 40, 40, PixelFormats.Default);
bity.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bity);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(frame);
MemoryStream ms = new MemoryStream();
encoder.Save(ms);
如果位图是300x300,为什么白色矩形(0,0,400,400)只占用它的一小部分? 为什么不是以文字为中心?
我甚至不确定Google的条款。我寻求智慧。
答案 0 :(得分:4)
注意:除了我的原始答案之外,在提供赏金后添加此内容
首先,不需要400x400背景矩形,因为你只渲染一个300x300的位图,所以这是第一次改变:
ctx.DrawRectangle(Brushes.White, new Pen(Brushes.White, 10), new System.Windows.Rect(0, 0, 300, 300));
通过这种改变,输出将完全相同,但它简化了解释。
在可能和合乎逻辑的情况下,WPF使用DIP(与设备无关的像素)作为度量单位而不是像素。当你这样做时:
<Rectangle Width="100" Height="100"/>
您不一定会得到100 {100}物理像素的Rectangle
。如果您的设备每个物理英寸具有多于(或少于)96个像素,那么您将获得不同数量的物理像素。我想,每英寸96像素是一种行业标准。智能手机和平板电脑等现代设备每个物理英寸的像素数量要多得多。如果WPF使用物理像素作为度量单位,那么前面提到的Rectangle
会在这样的设备上变小。
现在,为了渲染位图(或JPEG,PNG,GIF等),必须使用设备depdendent像素,因为它是栅格化格式(不是矢量格式)。这就是您在调用RenderTargetBitmap
构造函数时指定的内容。你告诉它你希望得到的位图是300x300物理像素,DPI为40.由于源的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物理像素的位图,并且源将始终完全填充它。
答案 1 :(得分:3)
当你想要96:
时,你已经指定了40 DPIRenderTargetBitmap bity = new RenderTargetBitmap(300, 300, 96, 96, PixelFormats.Default);