How to edit a WritableBitmap.BackBuffer file in a stream without an interface?

My application runs heavy algorithms to edit an image placed in a WPF window. I need the release to run in the background thread. However, when trying to edit a BackBuffer from a WritableBitmap on a non-interface, the stream throws an 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 read dozens of tutorials, all of which tell me to do a BackBuffer release in a user interface thread (i.e. MSDN ).

How to edit WritableBitmap.BackBuffer in a stream without a UI without any useless copy / clone buffer?

+6
source share
5 answers

MSDN suggests writing to the backbuffer in the background thread. In the user interface thread, only certain operations must be performed before and after the update. Therefore, when the background thread performs the actual update, the user interface thread may perform other actions:

  //Put this code in a method that is called from the background thread long pBackBuffer = 0, backBufferStride = 0; Application.Current.Dispatcher.Invoke(() => {//lock bitmap in ui thread _bitmap.Lock(); pBackBuffer = (long)_bitmap.BackBuffer;//Make pointer available to background thread backBufferStride = Bitmap.BackBufferStride; }); //Back to the worker thread unsafe { //Carry out updates to the backbuffer here foreach (var update in updates) { long bufferWithOffset = pBackBuffer + GetBufferOffset(update.X, update.Y, backBufferStride); *((int*)bufferWithOffset) = update.Color; } } Application.Current.Dispatcher.Invoke(() => {//UI thread does post update operations _bitmap.AddDirtyRect(new System.Windows.Int32Rect(0, 0, width, height)); _bitmap.Unlock(); }); 
+11
source

As Clemens said, this is not possible.

You have three options:

1) Make your editing in the buffer and blit when done, as Clemens prompts.

2) Make editing in very small chunks and assign them with good priority in the GUI thread. If you keep your working chunks small enough, the GUI will remain responsive, but obviously this complicates the editing code.

3) Combine 1 and 2. Edit the small pieces in another thread, then blit each piece upon completion. This allows you to maintain a graphical interface without using memory for a full backup buffer.

+3
source

You simply cannot write BackBuffer from a thread other than a UI.

In addition to what Klaus78 said, I would suggest the following approach:

  • Run asynchronous bitmap editing code in a separate buffer (for example, byte[] ) in the ThreadPool thread using QueueUserWorkItem . Do not create a new thread every time you need to perform an asynchronous operation. What ThreadPool was created for.

  • Copy the edited WritePixels buffer in the WriteableBitmap manager. No need to lock / unlock.

Example:

 private byte[] buffer = new buffer[...]; private void UpdateBuffer() { ThreadPool.QueueUserWorkItem( o => { // write data to buffer... Dispatcher.BeginInvoke((Action)(() => writeableBitmap.WritePixels(..., buffer, ...))); }); } 
+2
source

In WPF, cross-thread calls are made using the Dispatcher class.

In your case, in the no-UI stream, you need to get the instance of the stream manager in which WritableBitmap is created.

In this dispatcher then call Invoke (or BeginInvoke if you want it asynchronously)

The call then calls the delegate function where the BackBuffer is edited

0
source

I have done the following based on this answer :

In the view model, there is such a property that is associated with the image source in XAML:

 private WriteableBitmap cameraImage; private IntPtr cameraBitmapPtr; public WriteableBitmap CameraImage { get { return cameraImage; } set { cameraImage = value; cameraBitmapPtr = cameraImage.BackBuffer; NotifyPropertyChanged(); } } 

Using a property means that if WritableBitmap changes, for example. due to permission, it will be updated in the view and a new IntPtr will be created.

The image is built if necessary:

 CameraImage = new WriteableBitmap(2448, 2048, 0, 0, PixelFormats.Bgr24, null); 

In the update stream, a new image is copied, for example. via:

 [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")] public static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length); 

would you do

 CopyMemory(cameraImagePtr, newImagePtr, 2448 * 2048 * 3); 

It might be better for this ...

In the same stream, after the copy:

 parent.Dispatcher.Invoke(new Action(() => { cameraImage.Lock(); cameraImage.AddDirtyRect(new Int32Rect(0, 0, cameraImage.PixelWidth, cameraImage.PixelHeight)); cameraImage.Unlock(); }), DispatcherPriority.Render); 

where parent is the control / image window.

0
source

Source: https://habr.com/ru/post/911651/


All Articles