My application runs CPU-heavy algorythms to edit an Image placed at a WPF window. I need the edition to be done in a background thread. However trying to edit the BackBuffer of WritableBitmap in non UI thread throws InvalidOperationException.
private WriteableBitmap writeableBitmap;
private void button1_Click(object sender, RoutedEventArgs e)
{
// Create WritableBitmap in UI thread.
this.writeableBitmap = new WriteableBitmap(10, 10, 96, 96, PixelFormats.Bgr24, null);
this.image1.Source = this.writeableBitmap;
// Run code in non UI thread.
new Thread(
() =>
{
// 'Edit' bitmap in non UI thread.
this.writeableBitmap.Lock(); // Exception: The calling thread cannot access this object because a different thread owns it.
// ... At this place the CPU is highly loaded, we edit this.writeableBitmap.BackBuffer.
this.writeableBitmap.Unlock();
}).Start();
}
I have read dozens of manuals, all of them tells me to do the BackBuffer edition in UI thread (i.e MSDN).
How to edit the WritableBitmap.BackBuffer in a non UI thread without any useless buffer copying/cloning?
You simply can't write to BackBuffer from a non UI thread.
In addition to what Klaus78 said, i would suggest the following approach:
Perform asynchronous "bitmap editing" code on a separate buffer (e.g.
byte[]
) in a ThreadPool thread by means of QueueUserWorkItem. Do not create a new Thread every time you need to perform an asynchronous operation. That's what ThreadPool was made for.Copy the edited buffer by WritePixels in the WriteableBitmap's Dispatcher. No need for Lock/Unlock.
Example:
MSDN suggests writing to the backbuffer in a background thread. Only certain pre- and post update operations need to be carried out on the UI thread. So while the background thread is doing the actual updating, the UI thread is free to do other things:
I implemented the following, based on this answer:
In the view model, there is a property like this, that is bound to the Image source in XAML:
Using a property means that if the WritableBitmap changes, e.g. because of resolution, it would be updated in the View and also a new IntPtr will be constructed.
The image is constructed when appropriate:
In the update thread, a new image is copied in, e.g. using:
you would do
There might be a better function for this...
In the same thread, after the copy:
where
parent
is the Control / Window with the Image.In WPF cross thread calls are done using the Dispatcher class.
In your case in the no-UI thread you need to get the instance of the Dispatcher of the thread where WritableBitmap is created.
On that dispatcher then call Invoke (or BeginInvoke if you want it asynchron)
Invoke then calls a delegate function where the BackBuffer is edited
As Clemens said, this is impossible.
You have three choices:
1) Do your editing in a buffer and blit when finished as Clemens suggests.
2) Do the editing in very small chunks and schedule them at a nice priority on the GUI thread. If you keep your work chunks small enough, the GUI will remain responsive, but obviously this complicates the edit code.
3) Combine 1 & 2. Edit small chunks in another thread, then blit each chunk as it completes. This keeps GUI responsive without using memory for a full back buffer.