wpf RenderTargetBitmap 渲染错误大小的图像
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13144615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
RenderTargetBitmap renders image of a wrong size
提问by Cracker
My task is to show the user a thumbnail of each page of his XPS document. I need all the images to be small, so I render them with dpiset to 72.0 (I have googled that size of an A4 sheet with dpi 72.0 is 635x896). Basically, I do the following:
我的任务是向用户显示他的 XPS 文档每一页的缩略图。我需要所有的图像都很小,所以我用dpi设置为 72.0 来渲染它们(我用谷歌搜索了 dpi 72.0 的 A4 纸的尺寸是 635x896)。基本上,我执行以下操作:
List<BitmapImage> thumbnails = new List<BitmapImage>();
documentPaginator.ComputePageCount();
int pageCount = documentPaginator.PageCount;
for (int i = 0; i < pageCount; i++)
{
DocumentPage documentPage = documentPaginator.GetPage(i);
bool isLandscape = documentPage.Size.Width > documentPage.Size.Height;
Visual pageVisual = documentPage.Visual;
//I want all the documents to be less or equals to A4
//private const double A4_SHEET_WIDTH = 635;
//private const double A4_SHEET_HEIGHT = 896;
//A4 sheet size in px, considering 72 dpi
RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
(int)(System.Math.Min(documentPage.Size.Width, A4_SHEET_WIDTH)),
(int)(System.Math.Min(documentPage.Size.Height, A4_SHEET_HEIGHT)),
72.0, 72.0,
PixelFormats.Pbgra32);
targetBitmap.Render(pageVisual);
BitmapFrame frame = BitmapFrame.Create(targetBitmap);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(frame);
BitmapImage image = new BitmapImage();
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
encoder.Save(ms);
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
if (isLandscape)
{
image.Rotation = Rotation.Rotate270;
}
image.EndInit();
}
thumbnails.Add(image);
}
But when I render a document page (A4) its size is actually 846x1194instead of the one I expected. I tried to make the dpi lower (48.0) and the image's size has grown even bigger (I guess, I just do not quite undrestand what dpi is and how it affects the document). I tried to make dpi=96.0, and the size went smaller. I set one of images from the collection of instances of the class BitmapImagegenerated by the code above as source for the Imagecontrol (I am creating a WPF application) and in case dpi is set to 96.0 my program looks like this:
但是当我渲染文档页面 (A4) 时,它的大小实际上是846x1194,而不是我预期的大小。我试图降低 dpi (48.0) 并且图像的大小变得更大(我猜,我只是不太明白 dpi 是什么以及它如何影响文档)。我试图使dpi=96.0,尺寸变小了。我将BitmapImage上面代码生成的类的实例集合中的一个图像设置为Image控件的源(我正在创建一个 WPF 应用程序),如果 dpi 设置为 96.0,我的程序如下所示:

As you can see, part of the page is not shown at all, it does not fit inside the Imagecontrol, even though the size of the control is set to 635x896that's why according to the code above, the image must be displayed correctly and all the text must fit.
What result do I expect in a nutshell: I am trying to create thumbnails of the pages of a document, but I want them to be smaller, relative to some number (I am sorry, I am not quite sure how do I say such stuff in English, basically if the document's page width is 1200 px I want it to be 1200/n, where nis the "some number" that I mentioned earlier), but if the shrinked image's size is still bigger than 635x896I want the size to be 635x896.
Thanks in advance. And also I am sorry for my bad English.

如您所见,页面的一部分根本没有显示,它不适合Image控件内部,即使控件的大小设置为635x896这就是为什么根据上面的代码,图像必须正确显示并且所有文字必须适合。
简而言之,我期望什么结果:我正在尝试创建文档页面的缩略图,但我希望它们相对于某个数字更小(对不起,我不太确定我该怎么说这些东西在英文中,基本上如果文档的页面宽度是 1200 px 我希望它是1200/n,其中n是我之前提到的“某个数字”),但如果缩小图像的大小仍然大于635x896我希望大小为635x896.
提前致谢。我也很抱歉我的英语不好。
回答by Clemens
First of all, DPI means Dots Per Inch, or pixel per inch. In the case of rendering an A4 page - which is 21 by 29.7 cm - to a bitmap at 72 DPI, you would end up with a bitmap of the following size:
首先,DPI 的意思是 Dots Per Inch,即每英寸像素数。在将 A4 页面(21 x 29.7 厘米)渲染为 72 DPI 的位图的情况下,您最终会得到以下大小的位图:
- Width = 21 cm / (2.54 cm/inch) * 72 pixel/inch = 595 pixel
- Height = 29.7 cm / (2.54 cm/inch) * 72 pixel/inch = 842 pixel
- 宽度 = 21 厘米/(2.54 厘米/英寸)* 72 像素/英寸 = 595 像素
- 高度 = 29.7 厘米/(2.54 厘米/英寸)* 72 像素/英寸 = 842 像素
Besides this you shouldn't care too much about DPI, with one exception: WPF rendering is done at 96 DPI. This means that an A4-sized page of your document is rendered to a 794 x 1123 bitmap. As a reminder:
除此之外,您不必太在意 DPI,只有一个例外:WPF 渲染是在 96 DPI 下完成的。这意味着 A4 大小的文档页面将呈现为 794 x 1123 位图。提醒一句:
- Width = 21 cm / (2.54 cm/inch) * 96 pixel/inch = 794 pixel
- Height = 29.7 cm / (2.54 cm/inch) * 96 pixel/inch = 1123 pixel
- 宽度 = 21 厘米/(2.54 厘米/英寸)* 96 像素/英寸 = 794 像素
- 高度 = 29.7 厘米/(2.54 厘米/英寸)* 96 像素/英寸 = 1123 像素
Hence, 794 x 1123 should be the size of your RenderTargetBitmap when it contains a page that is exactly A4. If the page size is less than A4, the bitmap should be smaller. If on the other hand the page is larger than A4, it should be scaled downto 794 x 1123. And that is the trick. Instead of rendering the page visual directly into the RenderTargetBitmap, you can put the visual into a ContainerVisualwith a ScaleTransformas shown below.
因此,当 RenderTargetBitmap 包含正好为 A4 的页面时,794 x 1123 应该是它的大小。如果页面大小小于 A4,位图应该更小。另一方面,如果页面大于 A4,则应将其缩小到 794 x 1123。这就是诀窍。您可以将视觉效果放入带有ScaleTransform的ContainerVisual 中,而不是直接将页面视觉效果渲染到 RenderTargetBitmap 中,如下所示。
for (int i = 0; i < paginator.PageCount; i++)
{
DocumentPage page = paginator.GetPage(i);
double width = page.Size.Width;
double height = page.Size.Height;
double maxWidth = Math.Round(21.0 / 2.54 * 96.0); // A4 width in pixels at 96 dpi
double maxHeight = Math.Round(29.7 / 2.54 * 96.0); // A4 height in pixels at 96 dpi
double scale = 1.0;
scale = Math.Min(scale, maxWidth / width);
scale = Math.Min(scale, maxHeight / height);
ContainerVisual containerVisual = new ContainerVisual();
containerVisual.Transform = new ScaleTransform(scale, scale);
containerVisual.Children.Add(page.Visual);
RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)(width * scale), (int)(height * scale), 96, 96, PixelFormats.Default);
bitmap.Render(containerVisual);
...
}

