.NETで画像リソースやOpenCV の.NETラッパーEmgu から画像データを得ようとするとSystem.Drawing.Bitmapインスタンスになるのですが、WPFで直接読み込む方法がないようです。というわけで変換になるのですが、これがちと曲者っぽいので備忘録。
Stream経由
コードが短くて済むのは一旦StreamにBMP形式(昨今メモリは潤沢にあるのでPNG等圧縮する必要はないかと)で保存、それを読みこませる方法。
using System.Windows.Controls; using System.Drawing; using System.IO; (省略) Bitmap bmp = (何か); using (Stream st = new MemoryStream()) { bmp.Save(st, ImageFormat.Bmp); st.Seek(0, SeekOrigin.Begin); image1.Source = BitmapFrame.Create(st, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); }
この方法が曲者なのはBitmapFrame.CreateがStream読み込みを同期でするのか非同期なのかよく分からない所。非同期で上のコードだと読み込み完了前にStreamが破棄される可能性が高いので失敗(例外は出ず画像が表示されない)。とは言え読み込み終わってもStreamは自動的には閉じられないようなので、放おっておくとガベージコレクション任せということでしょうか。
ネット上の画像を同期読み込みするとBitmapFrame.Createで処理がブロックされるため非同期にするのでしょうけど、同期と非同期の切替条件が今ひとつはっきりしない。私の環境(Windows7 x64、Visual C# 2010 Express、.NET Framework 4 Client Profile)だとFileStreamとMemoryStreamは同期、WebResponseから得るStreamは非同期になるようでしたが、環境や将来的に変わるかもしれない。
非同期になると読み込み中はBitmapFrameインスタンスのIsDownloadingがtrueになり、完了時にDownloadCompletedイベントが飛ぶ(同期だと飛ばない)みたいなのでプロパティをチェックしてイベントハンドラでStreamを閉じるという方法もありますが、イベントハンドラをセットする前に読み込みが完了する可能性がゼロではないため、ちと不安。まぁCreateの直後にやればまず大丈夫でしょうけど。
HBitmap経由
Unmanagedに抵抗がなければ、こちらの方がある意味悩まなくて済むかと。
using System.Windows.Controls; using System.Drawing; using System.IO; (省略) [System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); (省略) Bitmap bmp = (何か); IntPtr hBitmap = bmp.GetHbitmap(); try { image1.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); } finally { DeleteObject(hBitmap); }
LateBoundBitmapDecoder なんてもの別途用意しているんだから、自動で同期・非同期を切り替えるのはやらなくていいと思う、個人的には。
[参考]
1. osamuk — 2013/07/22@21:01:00