InkCanvas の Background に 高DPI 画像を設定すると縮小表示される理由

## 理由

`InkCanvas.Background` (画像) に設定する `ImageBrush` の `Width`, `Height` は画像のピクセル数ではなく

`画像のピクセル数 * (96F / 画像の水平方向dpi or 垂直方向 dpi)` で計算されるから。これは WPF 自体の仕様。

なので、dpi の値が

* 96f 超ならば背景画像の 幅or高さは小さくなる
* 96f 未満ならば背景画像の 幅or高さは大きくなる

## 確認した現象

解像度 `640x480` だが dpi が異なる 2 枚の画像を用意する。

各々を `InkCanvas` へコードビハインドにて背景設定した場合

* 96dpi 画像の場合は InkCanvas 上で縮小されない
* 300dpi 画像の場合は InkCanvas 上で縮小される

| 96dpi | 300dpi |
| --- | --- |
| ![dog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31223/f9abe443-281e-de48-24de-8777731b712a.png) | ![owl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31223/fa47d0fc-0f94-9524-53e9-4ea6857a5af6.png) |

## 再現手順

* VisualStudio 2019
* .NET 4.7.2

(VisualStudio 2012, .NET 4.0 でも再現する)

後出サンプルソースの通り、 `InkCanvas.Background` の背景画像は次の流れで設定する。

* `FileStream` で読み込みしたものを、`BitmapImage` へ設定
* `ImageBrush` に上記 `BitmapImage` を設定する
  * `Stretch.None` で拡大縮小はさせない
* `InkCanvas.Background` に上記 `ImageBrush` を設定する

<details><summary>xaml</summary><div>

```xml
<Window x:Class="DpiTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DpiTest"
        mc:Ignorable="d"
        WindowStyle="None"
        Width="800"
        Height="600" >
    <Grid Background="LightGray">
        <InkCanvas
            Name="MainCanvas"
            Width="640"
            Height="480" />
    </Grid>
</Window>
```

</div></details>

<details><summary>xaml.cs</summary><div>

```cs
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;

namespace DpiTest
{
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      this.InitializeImage();
    }

    private void InitializeImage()
    {
      var bitmapImage = new BitmapImage();
      using (var fileStream = new FileStream(@"C:\path\to\image.jpg", FileMode.Open, FileAccess.Read))
      {
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.StreamSource = fileStream;
        bitmapImage.EndInit();
      }
      bitmapImage.Freeze();

      var imageBrush = new ImageBrush();
      imageBrush.Stretch = Stretch.None;
      imageBrush.ImageSource = bitmapImage;

      this.MainCanvas.Background = imageBrush;
    }
  }
}
```

</div></details>

## 理由を把握する為に確認・参考したもの

[WPF リポジトリ](https://github.com/dotnet/wpf)より

* [ImageSource の親 > BitmapSource.Width](https://github.com/dotnet/wpf/blob/ac9d1b7a6b0ee7c44fd2875a1174b820b3940619/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Imaging/BitmapSource.cs#L415)
  * 原寸のピクセルデータではなく、 `GetHeightInternal()` を取得していることを確認できる
* [原寸 pixel を変換する処理 その1](https://github.com/dotnet/wpf/blob/ac9d1b7a6b0ee7c44fd2875a1174b820b3940619/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Imaging/BitmapSource.cs#L458)
  * dpi と ピクセルデータを基に、変換処理を実行していることが分かる
* [原寸 pixel を変換する処理 その2](https://github.com/dotnet/wpf/blob/ac9d1b7a6b0ee7c44fd2875a1174b820b3940619/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/ImageSource.cs#L98)
  * 変換処理の内容。解説するよりも内容を見てもらったほうが良い

## 使った画像

* [蝶と犬](https://wallpapersafari.com/w/2PksJn)
* [フクロウ](https://pixabay.com/ja/photos/%E7%A9%B4%E3%82%92%E6%8E%98%E3%82%8B-%E3%83%95%E3%82%AF%E3%83%AD%E3%82%A6-%E6%8E%98%E3%82%8A%E9%80%B2%E3%81%BF%E3%81%BE%E3%81%99-2532410/)