0

リソースストリームから構築された BitmapImage があります。後処理のためにバックグラウンド ワーカー スレッドに送信する必要があります。しかし、ワーカー スレッドはビットマップ データにアクセスできず、LoadImage の最初の行でSystem.UnauthorizedAccessExceptionを取得しているようです。これを解決するには?また、処理されたビットマップを UI (XAML) に転送して表示する必要があります。それを正しく行う方法は?

    ImageLoader.RunWorkerAsync(albumArtImage);

    private void LoadImage(object sender, DoWorkEventArgs e)
    {
        WriteableBitmap wb = new WriteableBitmap((BitmapImage)e.Argument);
        wb.Resize(AppWidth, AppHeight, WriteableBitmapExtensions.Interpolation.Bilinear);
        var wb2 = WriteableBitmapExtensions.Convolute(wb, WriteableBitmapExtensions.KernelGaussianBlur3x3);            
        e.Result = wb2;
    }

    private void LoadImageCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        AlbumBackground.ImageSource = (WriteableBitmap)e.Result;
    }
4

3 に答える 3

1

UI スレッドから WriteableBitmap を呼び出す必要があります。これは、回避できないユーティリティの制限にすぎません。

MSDN から:

WriteableBitmap クラスは 2 つのバッファーを使用します。バック バッファはシステム メモリに割り当てられ、現在表示されていないコンテンツを蓄積します。フロント バッファはシステム メモリに割り当てられ、現在表示されているコンテンツが含まれます。レンダリング システムは、フロント バッファをビデオ メモリにコピーして表示します。

2 つのスレッドがこれらのバッファーを使用します。ユーザー インターフェイス (UI) スレッドは UI を生成しますが、画面には表示しません。UI スレッドは、ユーザー入力、タイマー、およびその他のイベントに応答します。アプリケーションは複数の UI スレッドを持つことができます。レンダリング スレッドは、UI スレッドからの変更を作成してレンダリングします。アプリケーションごとに 1 つのレンダリング スレッドしかありません。

UI スレッドはコンテンツをバック バッファーに書き込みます。レンダリング スレッドは、フロント バッファーからコンテンツを読み取り、それをビデオ メモリにコピーします。バック バッファーへの変更は、変更された四角形領域で追跡されます。

http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx

BackgroundWorker の代替:

UI スレッドでこれを行う必要があるという事実を回避することはできません。ただし、キューの最後にプッシュして、ユーザーが目立たないようにすることができます。

次のようなことを試してください:

public static void RunDelayedOnUiThread(Action action)
{
     Deployment.Current.Dispatcher.BeginInvoke(() =>
     {
         var timer = new DispatcherTimer 
                     { 
                         Interval = TimeSpan.FromMilliseconds(1) 
                     };
         timer.Tick += (sender, args) =>
         {
             timer.Stop();
             action();
         };

         timer.Start();
     });
}

画像処理 (この場合は渡されたアクション) が、UI スレッドで処理されるのを待っているキューの最後に配置されるようにするには、これで十分です。画像処理は、動作中の UI スレッドを引き続きブロックしますが、少なくとも他のすべてを最初に実行する必要があります。

使い方:

BeginInvoke を呼び出すと、アクションはディスパッチャーのキューの最後に配置され、UI スレッドが現在実行中 (コードの実行) から解放されたときに実行されます。しかし、BeginInvoke を再度 (そして何度も) 呼び出すと、画像処理によってディスパッチャのキュー内の他のものがブロックされる可能性があるため、これを再び後ろに置きたいと考えています。これが DispatcherTimer の出番です。 MSDN から:

タイマーは、時間間隔が発生したときに正確に実行されることは保証されていませんが、時間間隔が発生する前に実行されないことは保証されています。これは、DispatcherTimer 操作が他の操作と同様に Dispatcher キューに置かれるためです。DispatcherTimer 操作が実行されるタイミングは、キュー内の他のジョブとその優先度に依存します。

したがって、指定した 1 ミリ秒の間隔だけで、これをキューの最後に押し戻すのに十分なはずです。

Windows Phone 7 では、一部のタッチ イベントがブロックされる可能性がありますが、少なくともページのレンダリングがブロックされることはありません。

Windows Phone 8 では、Panorama、Pivo​​t、および LongListSelector はすべて非 UI スレッドでの入力に応答するため、少し安全になります。

于 2012-12-31T12:48:25.520 に答える
0

これは私にとってはうまくいきました。backgroundWorker に渡す前にビットマップをフリーズしますが、例外はスローされません。

BackgroundWorker bw = new BackgroundWorker();
BitmapSource bmpCanvas;
BitmapSource bmpRef;

List<object> arg = new List<object>();
arg.Add(bmpCanvas);
arg.Add(bmpRef);
bmpCanvas.Freeze();
bmpRef.Freeze();

bw.RunWorkerAsync(arg);

そして、backgroundworker でビットマップを使い終わったら、ビットマップをフリーズします。

void bw_DoWork(object sender, DoWorkEventArgs e)
{
   List<object> argu = e.Argument as List<object>;

   BitmapSource bCanvas = (BitmapSource)argu[0];
   BitmapSource bref = (BitmapSource)argu[1];
   // doing something with those images...
   bCanvas.Freeze();
   e.Result = bCanvas;
}

そして i RunWorkerCompleted のビットマップ イメージを更新します。

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
   BitmapSource bmpCanvas = (BitmapSource)e.Result;

   image.Source = bmpCanvas;
}

http://msdn.microsoft.com/en-us/library/ms750509.aspx

于 2013-01-29T01:43:41.190 に答える
0

ビットマップの変更が UI のみのプロセスであることに異議を唱える必要があります。別々のバックグラウンドワーカー内の別々のビットマップの変更パラメータを計算し、計算が完了したときにいくつかの配列(ピクセル割り当て用)にデータを保存し、いくつかのブール値を「while」および「if」として機能するように設定するいくつかのコード(おそらく投稿するには長すぎる)を実装しました。スイッチ。処理するビットマップごとに 1 つずつ、別の BGW 内に、計算完了ブール値がトリガーされるのを待っているループがあります。トリガーされると、セカンダリ ワーカーは配列から結果をコピーし、計算スレッドを再起動して次のフレームの計算を開始します... (これはアニメーションだと言いましたか??)。次に、これらの BGW は、tempbit.SetPixel(x,y,color) を使用して、コピーされたパラメーターを一時ビットマップ (元のビットマップごとに 1 つ) に適用します。(もっと効率的な方法があるかもしれません...)。ビットマップが更新されると、より多くのブール スイッチがスローされます (計算 BGW が次のフレームでクランキングしている間ずっと)。次に、最後の BGW で、個別の一時ビットマップが更新されるのを待つコードを実装します。完了したら、ビットマップをコピーし、イメージ更新 BGW を再起動します (このブール構造により、オブジェクトの重複が防止されます)。すべての画像がコピーされたら、画像を結合し、picturebox.Image を更新してからループを再開します (これには、さらにブール論理と goto ステートメントまたは非同期リレー メソッドの再起動が必要になります)。基本的に、2 つの backgroundworker オブジェクトが 3 番目の共有オブジェクトにアクセスできない限り、例外はスローされません。より多くのブール値スイッチがスローされます (計算 BGW が次のフレームでクランキングしている間ずっと)。次に、最後の BGW で、個別の一時ビットマップが更新されるのを待つコードを実装します。完了したら、ビットマップをコピーし、イメージ更新 BGW を再起動します (このブール構造により、オブジェクトの重複が防止されます)。すべての画像がコピーされたら、画像を結合し、picturebox.Image を更新してからループを再開します (これには、さらにブール論理と goto ステートメントまたは非同期リレー メソッドの再起動が必要になります)。基本的に、2 つの backgroundworker オブジェクトが 3 番目の共有オブジェクトにアクセスできない限り、例外はスローされません。より多くのブール値スイッチがスローされます (計算 BGW が次のフレームでクランキングしている間ずっと)。次に、最後の BGW で、個別の一時ビットマップが更新されるのを待つコードを実装します。完了したら、ビットマップをコピーし、イメージ更新 BGW を再起動します (このブール構造により、オブジェクトの重複が防止されます)。すべての画像がコピーされたら、画像を結合し、picturebox.Image を更新してからループを再開します (これには、さらにブール論理と goto ステートメントまたは非同期リレー メソッドの再起動が必要になります)。基本的に、2 つの backgroundworker オブジェクトが 3 番目の共有オブジェクトにアクセスできない限り、例外はスローされません。イメージ更新 BGW を再起動します (このブール構造により、オブジェクトのオーバーラップが防止されます)。すべての画像がコピーされたら、画像を結合し、picturebox.Image を更新してからループを再開します (これには、さらにブール論理と goto ステートメントまたは非同期リレー メソッドの再起動が必要になります)。基本的に、2 つの backgroundworker オブジェクトが 3 番目の共有オブジェクトにアクセスできない限り、例外はスローされません。イメージ更新 BGW を再起動します (このブール構造により、オブジェクトのオーバーラップが防止されます)。すべての画像がコピーされたら、画像を結合し、picturebox.Image を更新してからループを再開します (これには、さらにブール論理と goto ステートメントまたは非同期リレー メソッドの再起動が必要になります)。基本的に、2 つの backgroundworker オブジェクトが 3 番目の共有オブジェクトにアクセスできない限り、例外はスローされません。

簡単に言えば、私は別々のタスクに別々の BGW を用意し、スレッドの重複を防ぐロジックを実装しています。これにより、クロススレッドやビジーチェックの手間をかけずに、BGW クラスの非同期の利点を引き続き利用できます。

ハッピーコーディング!

于 2013-08-18T08:48:56.073 に答える