0

首先让我说我已经对此进行了广泛的搜索并找到了部分答案,但没有任何方法可以奏效。

我需要在我的 WPF 应用程序中显示未缩放的位图图像。我想将位图的 1 个像素映射到显示器的 1 个像素。我确实打算通过发送多个版本的位图来支持多种分辨率。But I want to know that, when a particular bitmap has been chosen, it will be rendered EXACTLY as it has been designed.

我克服 WPF 中发生的自动缩放的策略是查看自动应用的内容(通过 OS DPI 设置),然后将相反的 LayoutTransform 应用到我的窗口的最外层容器。

这可确保无论用户的 DPI 设置是什么,应用程序都会以 1:1 的 WPF 像素与硬件像素的比率呈现窗口内容。到目前为止,一切都很好。

该代码看起来像这样。(假设这是使用参数 1.0 调用的)。

    private void SetScale(double factor)
    {
        // First note the current window transform factor.
        // This is the factor being applied to the entire window due to OS DPI settings.
        Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
        double currentWindowTransformFactorX = m.M11;
        double currentWindowTransformFactorY = m.M22;

        // Now calculate the inverse.
        double currentWindowTransformInverseX = (1 / m.M11);
        double currentWindowTransformInverseY = (1 / m.M22);

        // This factor will put us "back to 1.0" in terms of a device-independent-pixel to physical pixel mapping.
        // On top of this, we can apply our caller-specified factor.
        double transformFactorX = currentWindowTransformInverseX * factor;
        double transformFactorY = currentWindowTransformInverseY * factor;

        // Apply the transform to the registered target container
        ScaleTransform dpiTransform = new ScaleTransform(transformFactorX, transformFactorY);
        if (dpiTransform.CanFreeze)
            dpiTransform.Freeze();

        this.pnlOutermost.LayoutTransform = dpiTransform;
    }

到这里为止,一切都很好。无论我将我的 Windows DPI 设置为什么,该主容器的内容总是完全相同的大小,并且位图被精确渲染。

有趣的来了。我想通过提供特定分辨率的艺术作品来支持不同的屏幕分辨率,并适当地缩放我的整个 UI。

事实证明 LayoutTransform 非常适合这个。因此,如果我用 1.25 或 1.5 或其他任何值调用上述方法,则整个 UI 会缩放,并且一切看起来都很完美……除了我的图像,即使我将源更改为图像完全适合新的缩放尺寸。

例如,假设我在 XAML 中有一个 100x100 的图像。我的作品有三种风格:100x100、125x125 和 150x150。当我缩放容纳图像的容器时,我还将该图像的源更改为适当的源。

有趣的是,如果图像对象所在的位置在按因子缩放时会产生积分结果,那么缩放后的图像看起来很好。也就是说,假设图像具有以下属性:

Canvas.Left = 12
Canvas.Top = 100

当我们应用 1.25 的因子时,得到 15 和 125,图像看起来很棒。但是如果图像移动了一个像素,就说:

Canvas.Left = 13
Canvas.Top = 100

现在,当我们应用 1.25 的因子时,我们得到 15.25 和 125,结果看起来很糟糕。

显然,这看起来像是某种舍入问题或类似问题。所以我试过:

UseLayoutRounding="True"
SnapsToDevicePixels="True"
RenderOptions.EdgeMode="Aliased" 
RenderOptions.BitmapScalingMode="NearestNeighbor"

我已经在窗口、正在缩放的​​容器和图像对象中尝试了这些。没有任何效果。无论如何,BitmapScalingMode 并没有真正的意义,因为图像根本不应该被缩放。

永远感谢任何能对此有所了解的人。

4

1 回答 1

0

我遇到了完全相同的问题,因此截至 2019 年,该问题似乎尚未在框架中得到修复。

我设法使用三步法解决了这个问题。

  1. 在我的顶级 UI 元素上启用布局舍入

    <UserControl ... UseLayoutRounding="True">
    
  2. 将反转LayoutTransform应用于我的Image对象(LayoutTransform应用于父对象ListBox)。

    <Image ... LayoutTransform="{Binding Path=LayoutTransform.Inverse,
                                 Mode=OneTime,
                                 RelativeSource={RelativeSource FindAncestor,
                                 AncestorType={x:Type ListBox}}}">
    
  3. 子类化Image并为OnRender.

        internal class CustomImage: Image {
            private PresentationSource presentationSource;
    
            public CustomImage() => Loaded += OnLoaded;
    
            protected override void OnRender(DrawingContext dc) {
                if (this.Source == null) {
                    return;
                }
                var offset = GetOffset();
                dc.DrawImage(this.Source, new Rect(offset, this.RenderSize));
            }
    
            private Point GetOffset() {
                var offset = new Point(0, 0);
    
                var root = this.presentationSource?.RootVisual;
                var compositionTarget = this.presentationSource?.CompositionTarget;
                if (root == null || compositionTarget == null) {
                    return offset;
                }
    
                // Transform origin to device (pixel) coordinates.
                offset = TransformToAncestor(root).Transform(offset);
                offset = compositionTarget.TransformToDevice.Transform(offset);
    
                // Round to nearest integer value.
                offset.X = Math.Round(offset.X);
                offset.Y = Math.Round(offset.Y);
    
                // Transform back to local coordinate system.
                offset = compositionTarget.TransformFromDevice.Transform(offset);
                offset = root.TransformToDescendant(this).Transform(offset);
    
                return offset;
            }
    
            private void OnLoaded(object sender, RoutedEventArgs e) {
                this.presentationSource = PresentationSource.FromVisual(this);
                InvalidateVisual();
            }
        }
    }
    

第 3 步中的代码基于此blogpost

通过CustomImage在我的 XAML 中使用类而不是Image绑定到BitmapSource将根据当前比例因子返回适当大小的图像的类,我设法在没有任何不需要的缩放的情况下实现了漂亮的图像。

InvalidateVisual请注意,当需要重新渲染图像时,您可能需要调用图像。

于 2019-11-14T09:55:22.513 回答