3

ユーザーが 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 コントロールがそれらを使用しなくなった場合、それらをメモリに保持したくありません。

4

2 に答える 2

2

I did a simple test (one image) with using DecodePixelWidth vs settings the Source on the XAML, and loading in with DecodePixelWidth took 28MB vs 178MB without the scaling down. I'm pretty sure it doesn't keep the original image in memory.

Since you said you're working with multiple images, I suspect that this is an image re-use issue. By default, WPF will cache a BitmapImage object (whether created by code or in XAML). It looks at the SourceUri as well as the DecodePixelWidth and DecodePixelHeight to find a match. If your TargetWidth and TargetHeight are changing, it would mean that WPF can't re-use its image cache; something that would not be a problem if you set the source without any extra options.

于 2010-03-25T21:14:05.920 に答える
0

私も同じ問題に直面し、BitmapImageプロパティの CreateOptions = をBitmapCreateOptions.IgnoreColorProfileに設定すると、より高速に動作するようになります。

頻繁に使用される BitmapImages のキャッシュを作成できるその他のこと。WPF がこれを自動的に行うべきであることは知っていますが、より速く動作すると思います。誰かが読み込み時間を測定しようとする場合は、コメントを書いてください:)

于 2013-10-23T22:35:04.567 に答える