ユーザーが ItemsControl を使用して複数の画像を操作できるようにするアプリケーションに取り組んでいます。いくつかのテストの実行を開始したところ、アプリに大きな画像の表示に問題があることがわかりました。http://earthobservatory.nasa.gov/Features/BlueMarble/BlueMarble_monthlies.phpからの高解像度 (21600x10800)、20MB の画像では機能しませんでしたが 、 http:// zebu からの 6200x6200、60MB のハッブル望遠鏡の画像を表示します。 uoregon.edu/hudf/hudf.jpg大丈夫です。
元のソリューションでは、(バインドを介して) ディスク上のファイルを指す Source プロパティを使用して Image コントロールを指定しただけです。Blue Marble ファイルでは、画像が表示されません。これは、ファンキーな MVVM + XAML 実装の奥深くに隠された単なるバグである可能性があります。Snoop によって表示されるビジュアル ツリーは次のようになります。
Window/Border/AdornerDecorator/ContentPresenter/Grid/Canvas/UserControl/Border/ContentPresenter/Grid/Grid/Grid/Grid/Border/Grid/ContentPresenter/UserControl/UserControl/Border/ContentPresenter/Grid/Grid/Grid/Grid/Viewbox/ ContainerVisual/UserControl/Border/ContentPresenter/Grid/Grid/ItemsControl/Border/ItemsPresenter/Canvas/ContentPresenter/Grid/Grid/ContentPresenter/Image...
これをデバッグしてください!WPFはそのようにクレイジーになる可能性があります...
とにかく、単純な WPF アプリケーションを作成すると、画像が問題なく読み込まれることがわかりました。根本的な原因を見つけようとしましたが、何週間も費やしたくありません。コンバーターを使用して画像を縮小するのが正しいことかもしれないと考えました-これが私がやったことです:
ImagePath = @"F:\Astronomical\world.200402.3x21600x10800.jpg";
TargetWidth = 2800;
TargetHeight = 1866;
と
<Image>
<Image.Source>
<MultiBinding Converter="{StaticResource imageResizingConverter}">
<MultiBinding.Bindings>
<Binding Path="ImagePath"/>
<Binding RelativeSource="{RelativeSource Self}" />
<Binding Path="TargetWidth"/>
<Binding Path="TargetHeight"/>
</MultiBinding.Bindings>
</MultiBinding>
</Image.Source>
</Image>
と
public class ImageResizingConverter : MarkupExtension, IMultiValueConverter
{
public Image TargetImage { get; set; }
public string SourcePath { get; set; }
public int DecodeWidth { get; set; }
public int DecodeHeight { get; set; }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
this.SourcePath = values[0].ToString();
this.TargetImage = (Image)values[1];
this.DecodeWidth = (int)values[2];
this.DecodeHeight = (int)values[3];
return DecodeImage();
}
private BitmapImage DecodeImage()
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = (int)DecodeWidth;
bi.DecodePixelHeight = (int)DecodeHeight;
bi.UriSource = new Uri(SourcePath);
bi.EndInit();
return bi;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
これで、1 つの「小さな」問題を除いて、問題なく動作します。Image.Source でファイル パスを指定するだけで、BitmapImage.DecodePixelWidth を使用する場合よりも、アプリケーションが実際に使用するメモリが少なくなり、高速に動作します。さらに、同じ画像を指す複数の Image コントロールがある場合は、Image.Source を使用します。それらは、1 つの画像のみが読み込まれた場合と同じだけのメモリを使用します。BitmapImage.DecodePixelWidth ソリューションでは、Image コントロールを追加するたびに、Image.Source を指定した場合よりも多くのメモリが使用されます。おそらく、WPF は何らかの形でこれらの画像を圧縮形式でキャッシュしますが、デコードされたサイズを指定すると、圧縮されていない画像をメモリに取得するように感じられ、さらに 6 倍の時間がかかります (おそらくそれがなければスケーリングは GPU で行われますか?)。
画像を縮小して一時ファイルに保存し、 Image.Source を使用してファイルを指定すると、おそらく動作しますが、かなり遅くなり、一時ファイルのクリーンアップを処理する必要があります。適切にロードされていない画像を検出できた場合、必要に応じて縮小することしかできなかったかもしれませんが、Image.ImageFailed がトリガーされることはありません。多分それはビデオ メモリと関係があり、このアプリは深いビジュアル ツリー、不透明度マスクなどでそれをさらに使用しているだけです。
実際の質問: 元の画像よりも低い特定の解像度でのみ必要な場合に、追加のコピーにメモリを追加したり、縮小された画像に追加のメモリを使用したりせずに、Image.Source オプションと同じくらい速く大きな画像を読み込むにはどうすればよいですか? また、Image コントロールがそれらを使用しなくなった場合、それらをメモリに保持したくありません。