【问题标题】:Saving DrawingVisual to an image file with a given DPI将 DrawingVisual 保存到具有给定 DPI 的图像文件
【发布时间】:2019-10-13 08:00:20
【问题描述】:

我有一个 WPF 应用程序,我想将 Canvas 保存到具有正确 DPI 值的文件中。画布尺寸是真实的物理尺寸,例如。 20x10 厘米,300 DPI,所以是 2362x1181。

我使用 DrawingVisual 绘制图像和形状,然后创建 RenderTargetBitmap。 rtb 的大小就是 Canvas 的大小。 dpiX 和 dpiY 是 96。我只有 96 DPI 才能获得正确的图像分辨率。当我将它设置为 300 时,画布变得高档,并裁剪为 2362x1181。所以,不好。我尝试通过dpi因子修改画布宽度和高度值,但也没有工作。

在 RenderTargetBitmap 之后,我使用 BitmapEncoder、BitmapFrame 和 BinaryWriter。请参阅下面的代码。工作得很好,但图像 DPI 值将是 96。我已经阅读了大量关于读取 DPI、重新保存图像、使用 SetResolution 的主题,但我不想降低质量,我不想读取文件并重新保存它,我不想改变像素宽度/高度等。

我真的很想用给定的 DPI 保存一个 DrawingVisual。我可以为“X 分辨率”(uint=282)和“Y 分辨率”(uint=283)编写 EXIF 数据,但这不会影响图像 DPI 设置,例如。 Photoshop 将读取 96,而不是 300。

BitmapEncoder encoder = new JpegBitmapEncoder();
BitmapFrame bFrame = BitmapFrame.Create(rtb, null, meta, icc);
encoder.Frames.Add(bFrame);

using (var stream = new MemoryStream())
{
encoder.Save(stream);
byte[] imageData = stream.ToArray();
using (FileStream fs = new FileStream(path, ...)
{
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(imageData);
bw.Close();
}
}

【问题讨论】:

    标签: c# .net wpf


    【解决方案1】:

    以下方法将 UIElement 保存到具有指定 DPI 值的 JPEG 文件中:

    public static void SaveElement(UIElement element, double dpi, string path)
    {
        var visual = new DrawingVisual();
        var width = element.RenderSize.Width;
        var height = element.RenderSize.Height;
    
        using (var context = visual.RenderOpen())
        {
            context.DrawRectangle(new VisualBrush(element), null,
                new Rect(0, 0, width, height));
        }
    
        var bitmap = new RenderTargetBitmap(
            (int)(width * dpi / 96), (int)(height * dpi / 96),
            dpi, dpi, PixelFormats.Default);
        bitmap.Render(visual);
    
        var encocer = new JpegBitmapEncoder();
        encocer.Frames.Add(BitmapFrame.Create(bitmap));
    
        using (var stream = File.OpenWrite(path))
        {
            encocer.Save(stream);
        }
    }
    

    或者,将 DPI 缩放应用到 DrawingVisual:

    public static void SaveElement(UIElement element, double dpi, string path)
    {
        var visual = new DrawingVisual();
        var width = element.RenderSize.Width;
        var height = element.RenderSize.Height;
    
        using (var context = visual.RenderOpen())
        {
            context.DrawRectangle(new VisualBrush(element), null,
                new Rect(0, 0, width / dpi * 96, height / dpi * 96));
        }
    
        var bitmap = new RenderTargetBitmap(
            (int)width, (int)height, dpi, dpi, PixelFormats.Default);
        bitmap.Render(visual);
    
        var encocer = new JpegBitmapEncoder();
        encocer.Frames.Add(BitmapFrame.Create(bitmap));
    
        using (var stream = File.OpenWrite(path))
        {
            encocer.Save(stream);
        }
    }
    

    【讨论】:

    • 是的,我也玩过 dpi 比例,但我的画布需要是最终的像素大小(例如 2362x1181),因为我在绘图时需要真实的坐标和图形大小,因此需要一个尺寸 * dpi / 96 调整将生成 7381x3690 图像。所以,这是 DPI 很好但图像过大的版本。
    • @Tamakwa 然后缩放矩形,而不是位图。查看编辑
    【解决方案2】:

    看来解决方案毕竟是 Bitmap SetResolution。我测试了一下,JpegBitmapEncoder() 之后好像不影响画质!并且图像分辨率不变,保留元数据,只有 DPI 会改变!

    帮助此文档:https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level

    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
    encoder.QualityLevel = 100;
    BitmapFrame bFrame = BitmapFrame.Create(rtb, null, meta, icc);
    encoder.Frames.Add(bFrame);
    
    using (var stream = new MemoryStream())
    {
        encoder.Save(stream);
    
        using (var bmpOutput = new System.Drawing.Bitmap(stream))
        {
            System.Drawing.Imaging.ImageCodecInfo myEncoder = GetEncoder(ImageFormat.Jpeg); 
    
            var encoderParameters = new System.Drawing.Imaging.EncoderParameters(1);
            encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
    
            bmpOutput.SetResolution(300.0f, 300.0f);
            bmpOutput.Save(filePath, myEncoder, encoderParameters);
        }
    }
    

    【讨论】:

      【解决方案3】:

      我尝试对 DrawingVisual 应用 DPI 缩放,但它产生的图像质量较差。 :( 我还尝试将画布设置为 756x378(96 DPI 大小),然后将 RenderTargetBitmap 设置为 2362x1181 和 300 DPI,但这也产生了较低的图像质量。所以,似乎任何缩小然后放大或放大然后缩小最好的解决方案。DrawingVisual 必须是最终渲染大小。

      【讨论】:

        猜你喜欢
        • 2019-11-03
        • 1970-01-01
        • 2013-05-31
        • 1970-01-01
        • 2011-05-08
        • 1970-01-01
        • 2021-07-11
        • 1970-01-01
        相关资源
        最近更新 更多