MemoryStreamにPngBitmapEncoderでSaveしたのを、BitmapFrame.CreateでBitmapに復元して確認した
結果

sourceDpiが元BitmapSourceのDpiで96
sourcePFが元BitmapSourceのピクセルフォーマット
png、bmp, tiff, gifがそれぞれのEncoderによる結果のピクセルフォーマットとDpi
まとめると

Dpiが変化していないのはTiffとGifだけど、Gifは96固定みたいで96以外でもすべて96に変化してた。ってことはDpiに関してはTiffでエンコードすれば良い
ピクセルフォーマットは変化していないほうが少ないけど、ここでもTiffが一番いい
Pbgra32はWPF独自っぽいから全滅なのかも、Pbgra32がBgra32に変換されると半透明の色が誤差程度だけど変化してしまうのが痛い
それ以外の変換は上位互換みたいなものだから問題なさそう
環境
- Windows 10 Home バージョン 22H2
- Visual Studio Community 2026 Version 18.7.2
- WPF
- C#
- .NET 10.0
テストアプリのコード
2026WPF/20260628_PngDpi at master · gogowaten/2026WPF · GitHub
using System.IO; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; namespace _20260628_PngDpi { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DpiTest(); } private void DpiTest() { // 1x1ピクセルのBitmapSource作成 byte[] pixels = [200, 1, 1, 1]; //BitmapSource source = BitmapSource.Create(1, 1, 96.0, 96.0, PixelFormats.Pbgra32, null, pixels, 4); //BitmapSource source = BitmapSource.Create(1, 1, 96.0, 96.0, PixelFormats.Bgra32, null, pixels, 4); //BitmapSource source = BitmapSource.Create(1, 1, 96.0, 96.0, PixelFormats.Rgb24, null, pixels, 3); BitmapSource source = BitmapSource.Create(1, 1, 96.0, 96.0, PixelFormats.Gray8, null, pixels, 4); double sourceDpi = source.DpiX; var sourcePF = source.Format; var png = GetFormatAndDpi(source, new PngBitmapEncoder()); var bmp = GetFormatAndDpi(source, new BmpBitmapEncoder()); var tiff = GetFormatAndDpi(source, new TiffBitmapEncoder()); var gif = GetFormatAndDpi(source, new GifBitmapEncoder()); //var jpegDpi = GetDpi(source, new JpegBitmapEncoder()); // デコード時にヘッダーエラーとかになる } private (PixelFormat, double) GetFormatAndDpi(BitmapSource source, BitmapEncoder encoder) { // 指定エンコーダで変換した後、復元したBitmapSourceのピクセルフォーマットとdpiを返す // 変換 using MemoryStream stream = new(); encoder.Frames.Add(BitmapFrame.Create(source)); encoder.Save(stream); // 復元 BitmapFrame bmp = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); var format = bmp.Format; return (bmp.Format, bmp.DpiX); } //private double GetDpi(BitmapSource source, BitmapEncoder encoder) //{ // encoder.Frames.Add(BitmapFrame.Create(source)); // using MemoryStream stream = new(); // encoder.Save(stream); // BitmapDecoder decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); // return decoder.Frames[0].DpiX; //} } }
感想
個人的に画像は写真画像以外はpngを使うことが多いから、png形式での保存でDpiが変化してしまうのが残念、回避方法は検索しても見つからず、WPFだけでは無理みたい
変化すると言っても誤差みたいなもので、アプリによっては96dpiって表示されるし、実際の画像の表示も問題ないけど、気になる
どこかに説明しているとこないかなあ
関連記事
143日前
gogowaten.hatenablog.com
7年前
半透明画像とWPFのPixelFormats.Pbgra32は相性が良くないかも - 午後わてんのブログ
gogowaten.hatenablog.com