首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在非UI线程中编辑WritableBitmap.BackBuffer?

如何在非UI线程中编辑WritableBitmap.BackBuffer?
EN

Stack Overflow用户
提问于 2012-03-26 16:37:36
回答 5查看 9.7K关注 0票数 8

我的应用程序运行占用大量CPU的算法来编辑放置在WPF窗口中的图像。我需要的编辑是在一个后台线程。然而,试图在非UI线程中编辑WritableBitmap的BackBuffer会抛出InvalidOperationException。

代码语言:javascript
复制
    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();
    }

我已经读了几十本手册,它们都告诉我在UI线程(即MSDN)中做BackBuffer版。

如何在没有任何无用的缓冲区复制/克隆的情况下在非UI线程中编辑WritableBitmap.BackBuffer?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-09-26 00:46:23

在后台线程中写入到后台缓冲区的MSDN suggests。只需要在UI线程上执行某些更新前和更新后操作。因此,当后台线程执行实际更新时,UI线程可以自由执行其他操作:

代码语言:javascript
复制
        //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
EN

Stack Overflow用户

发布于 2012-03-26 17:34:40

正如克莱门斯所说,这是不可能的。

您有三个选择:

1)按照Clemens的建议,在缓冲区中进行编辑,并在编辑完成后进行blit。

2)以非常小的块进行编辑,并在GUI线程上以很好的优先级调度它们。如果您保持工作块足够小,GUI将保持响应,但显然这会使编辑代码变得复杂。

3)组合1和2。在另一个线程中编辑小块,然后在完成时对每个块进行blit。这使GUI在不使用内存作为完全后台缓冲区的情况下保持响应。

票数 4
EN

Stack Overflow用户

发布于 2012-03-26 17:13:20

除了Klaus78所说的之外,我还建议采取以下方法:

  1. 通过QueueUserWorkItemThreadPool线程中的单独缓冲区(例如byte[])上执行异步“位图编辑”代码。不要在每次需要执行异步操作时都创建新的Thread。这就是ThreadPool诞生的目的。

  1. 在可写位图的调度程序中复制WritePixels编辑的缓冲区。不需要Lock/Unlock.

示例:

代码语言:javascript
复制
private byte[] buffer = new buffer[...];

private void UpdateBuffer()
{
    ThreadPool.QueueUserWorkItem(
        o =>
        {
            // write data to buffer...
            Dispatcher.BeginInvoke((Action)(() => writeableBitmap.WritePixels(..., buffer, ...)));
        });
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9868929

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档