はじめに
WPF で画像ビューワを作っていて、タイトルの通りのところでつまったので、まとめておきます。
画像ファイルを読み取って Image コントロールに表示する。
Image という名前の Image コントロールが Form に配置されていて、 path 変数に画像のファイルパスが入っている状態だとして
Image.Source = new BitmapImage(new Uri(path));
これで表示することができます。簡単です。
ですが、罠がありまして、ファイル閲覧中はファイル削除できないとか、そういうロックされる動作になります。
画像ビューアとして使い勝手悪いので、改善します。
画像ファイルのロックを解除する。
ここに書いてあるとおりにやればうまくいきます。
ファイルロックのかからない画像表示方法
http://neareal.net/index.php?Programming%2F.NetFramework%2FWPF%2FWriteableBitmap%2FLoadReleaseableBitmapImage
Exif の回転情報を読み取る
英語ですがこちらに載っています。
JPEG "Orientation" from EXIF for automatic rotation
https://social.msdn.microsoft.com/Forums/en-US/f2e4f23c-f6fd-4474-b47d-b4280f094c8f/jpeg-orientation-from-exif-for-automatic-rotation?forum=wpf
10サイトくらい調べましたが、正確に書かれていて動作するのは、ここの情報くらいです。
画像に回転や左右上下反転の加工をする
方法 : BitmapImage に変換を適用する | Microsoft Docs
https://docs.microsoft.com/ja-jp/dotnet/framework/wpf/graphics-multimedia/how-to-apply-a-transform-to-a-bitmapimage
このあたりを参考にしました。
#ソース
全体的に、再利用しやすいようにライブラリとして、ラッピングしました。
//----------------------------------------
//BitmapSorce
//----------------------------------------
//単純なImageコントロールへの画像のセット
BitmapSource GetImageSourceLock(string path,
Rotation rotation = Rotation.Rotate0) {
var bmpImage = new BitmapImage(new Uri(path));
bmpImage.Rotation = rotation;
return bmpImage;
}
//画像ファイルロックをしないコントロールへの画像のセット
BitmapSource GetImageSource(string path,
Rotation rotation = Rotation.Rotate0) {
var bmpImage = new BitmapImage();
FileStream stream = File.OpenRead(path);
bmpImage.BeginInit();
bmpImage.CacheOption = BitmapCacheOption.OnLoad;
bmpImage.StreamSource = stream;
bmpImage.Rotation = rotation;
bmpImage.EndInit();
stream.Close();
return bmpImage;
}
//画像ファイルロックをしないコントロールへの画像のセット
//Exif情報を読み取って自動で回転や左右上下反転を行う
BitmapSource GetImageSourceAutoExifOrientate(string path) {
var bmpImage = new BitmapImage();
FileStream stream = File.OpenRead(path);
bmpImage.BeginInit();
bmpImage.CacheOption = BitmapCacheOption.OnLoad;
bmpImage.StreamSource = stream;
var metaData = (BitmapFrame.Create(stream).Metadata) as BitmapMetadata;
stream.Position = 0;
bmpImage.EndInit();
stream.Close();
string query = "/app1/ifd/exif:{uint=274}";
if (!metaData.ContainsQuery(query)) {
return bmpImage;
}
switch (Convert.ToUInt32(metaData.GetQuery(query))) {
case 1:
return bmpImage;
case 3:
return TransformBitmap(
bmpImage,
new RotateTransform(180));
case 6:
return TransformBitmap(
bmpImage,
new RotateTransform(90));
case 8:
return TransformBitmap(
bmpImage,
new RotateTransform(270));
case 2:
return TransformBitmap(
bmpImage,
new ScaleTransform(-1, 1, 0, 0));
case 4:
return TransformBitmap(
bmpImage,
new ScaleTransform(1, -1, 0, 0));
case 5:
return TransformBitmap(
TransformBitmap(
bmpImage,
new RotateTransform(90)
),
new ScaleTransform(-1, 1, 0, 0));
case 7:
return TransformBitmap(
TransformBitmap(
bmpImage,
new RotateTransform(270)
),
new ScaleTransform(-1, 1, 0, 0));
}
return bmpImage;
}
BitmapSource TransformBitmap(BitmapSource source, Transform transform) {
var result = new TransformedBitmap();
result.BeginInit();
result.Source = source;
result.Transform = transform;
result.EndInit();
return result;
}
void ShowImage(string path) {
if (File.Exists(path)) {
Image.Source = GetImageSourceAutoExifOrientate(path);
} else {
Image.Source = null;
}
}
ShowImage が画面表示する部分です。次のようになっています。
Image.Source = GetImageSourceAutoExifOrientate(path);
ここを、下記のようにしても動作させることができます。
Image.Source = GetImageSourceLock(path);
Image.Source = GetImageSource(path);
もちろん、Exif 情報は読み取りません。外部から回転方向を指定できますが、あんまり意味がないかもしれません。実装上、作りやすかったからつけただけの機能です。