为什么所有的坐标和尺寸都很奇怪?
该代码生成了以下图像。
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)只占一小部分吗? 为什么文本不居中?
我什至不确定谷歌的条款是什么。我寻求智慧。
This code produced the following image.
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);
If the bitmap is 300x300, why does the white rectangle (0, 0, 400, 400) take up only a small portion of it? Why isn't the text centered?
I'm not even sure what terms to Google. I seek wisdom.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
注意:除了我原来的答案之外,在提供的赏金之后添加此内容
对于初学者来说,不需要 400x400 背景矩形,因为您只渲染 300x300 位图,所以这是第一个更改
:改变位置,输出将完全相同,但它简化了解释。
在可能且合乎逻辑的情况下,WPF 使用 DIP(与设备无关的像素)而不是像素作为度量单位。当您这样做时:
您不一定会得到一个 100x100 物理像素的
矩形
。如果您的设备每物理英寸的像素多于(或少于)96,那么您最终会得到不同数量的物理像素。我猜每英寸 96 像素是一种行业标准。智能手机和平板电脑等现代设备每物理英寸的像素要高得多。如果 WPF 使用物理像素作为其度量单位,则上述Rectangle
在此类设备上会呈现得更小。现在,为了渲染位图(或 JPEG、PNG、GIF 等),必须使用设备相关的像素,因为它是光栅化格式(不是矢量格式)。这就是您在调用 RenderTargetBitmap 构造函数时所指定的内容。您告诉它您希望生成的位图为 300x300 物理像素,DPI 为 40。由于源的 DPI 为 96(假设您的显示器是行业标准)而目标的 DPI 为 40,因此它必须缩小源以适应目标。因此,效果是渲染位图中缩小的图像。
现在您真正想要做的是确保源 DPI 和目标 DPI 匹配。它不像硬编码 96 那么简单,因为正如所讨论的,这只是一个标准 - 源实际上可能具有比这更高或更低的 DPI。不幸的是,WPF 没有提供获取 DPI 的好方法,我认为这很荒谬。但是,您可以执行一些 p/invoke 来获取它:
所以现在您可以将相关代码行更改为:
无论您在什么设备上运行,它都会工作。您最终总会得到一个 300x300 物理像素的位图,并且源将始终准确地填充它。
NOTE: adding this after bounty offered in addition to my original answer
For starters, there is no need for the 400x400 background rectangle because you're only rendering a 300x300 bitmap, so here's the first change:
With this change in place, the output will be exactly the same, but it simplifies the explanation.
Where possible and logical, WPF uses DIPs (device-independent pixels) as units of measure rather than pixels. When you do this:
You won't necessarily end up with a
Rectangle
that is 100x100 physical pixels. If your device has more (or fewer) than 96 pixels per physical inch, then you will end up with a different number of physical pixels. 96 pixels per inch is sort of an industry standard, I guess. Modern devices such as smart phones and tablets have far more pixels per physical inch. If WPF used physical pixels as its unit of measure, then the afore-mentionedRectangle
would render smaller on such a device.Now, in order to render a bitmap (or JPEG, PNG, GIF, whatever), device-depdendent pixels must be used because it's a rasterized format (not a vector format). And that's what you're specifying when you invoke the
RenderTargetBitmap
constructor. You're telling it you want the resultant bitmap to be 300x300 physical pixels with a DPI of 40. Since the source has a DPI of 96 (assuming your monitor is industry standard) and the target has a DPI of 40, it must shrink the source to fit the target. Hence, the effect is a shrunken image within your rendered bitmap.Now what you really want to do is ensure the source DPI and target DPIs match. It's not as simple as hard-coding 96 because, as discussed, that's just a standard - the source could actually have more or less DPI than that. Unfortunately, WPF doesn't provide a nice way of getting the DPI, which is ridiculous in my opinion. However, you can do a bit of p/invoke to get it:
So now you can change the relevant line of code to this:
And it will work regardless of the device on which you're running. You'll always end up with a bitmap that is 300x300 physical pixels, and the source will always fill it exactly.
当您需要 96 DPI 时,您指定了 40 DPI:
You've specified 40 DPI when you will want 96: