Image within a WPF application displays smaller than when viewed in external viewer

扶醉桌前 提交于 2019-12-01 05:30:42

Use a DPI of 96. WPF is scaling your image based on the size in inches, while the image viewer is displaying pixels. On most Windows systems, the screen resolution is assumed to be 96 DPI, so using that in your image will result in a one-to-one translation.

joshuapoehls

Thanks Mark! I did some Googling based on your info and found this article that provided a solution to get the result I wanted. This is starting to make sense now...

Edit: Linkrot. Pasting the critical text from the article here for reference....

<Image Source=”{Binding …}”
       Stretch=”Uniform”
       Width=”{Binding Source.PixelWidth,RelativeSource={RelativeSource Self}}”
       Height=”{Binding Source.PixelHeight,RelativeSource={RelativeSource Self}}” />

Here we’ve set Stretch to Uniform and bound the Width and Height to the PixelWidth and >PixelHeight of the Source, effectively ignoring DPI. The image however will not be pixel >perfect, even when using SnapToDevicePixels (which simply snaps the borders, not pixels >within the image). However, WPF in 3.5 SP1 will support a NearestNeighbor >BitmapScalingMode, which should correct this.

Alternatively, you could extend Image and implement MeasureOverride and ArrangeOverride to change the effect of the image's DPI:

class DpiAgnosticImage : Image
{
    protected override Size MeasureOverride(Size constraint)
    {
        var bitmapImage = Source as BitmapImage;

        var desiredSize = bitmapImage == null 
            ? base.MeasureOverride(constraint) 
            : new Size(bitmapImage.PixelWidth, bitmapImage.PixelHeight);

        var dpiScale = MiscUtil.GetDpiScale(this);
        desiredSize = new Size(desiredSize.Width / dpiScale.Width, desiredSize.Height / dpiScale.Height);

        desiredSize = ImageUtilities.ConstrainWithoutDistorting(desiredSize, constraint);

        if (UseLayoutRounding)
        {
            desiredSize.Width = Math.Round(desiredSize.Width);
            desiredSize.Height= Math.Round(desiredSize.Height);
        }

        return desiredSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        return new Size(Math.Round(DesiredSize.Width), Math.Round(DesiredSize.Height));
    }
}

Use it in xaml as if it were an Image:

<Grid>
  <local:DpiAgnosticImage
    Stretch="None"
    Source="{Binding ViewImage}">
    <Image.RenderTransform>
      <ScaleTransform
        x:Name="SomeName"/>
    </Image.RenderTransform>
  </local:DpiAgnosticImage>
</Grid>

Flaws to above code (that I know of):

  • Ignores Stretch
  • Assumes Source is a BitmapImage

=== Edit - Will's comment suggests he would like to know what is in GetDpiScale()

    public static Size GetDpiScale(Visual visual)
    {
        var source = PresentationSource.FromVisual(visual);

        var dpiScale = new Size(
            source.CompositionTarget.TransformToDevice.M11,
            source.CompositionTarget.TransformToDevice.M22);

        return dpiScale;
    }

This is the result of the .jpg file itself specifying the DPI - WPF is simply obeying. Here is a forum post detailing the problem with some solutions:

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!