c# - 如何将调色板图像从另一个线程生成到 WPF UI 线程?
问题描述
我想异步创建/操作基于调色板的图像并将该图像生成到 WPF UI 线程。
为了从另一个线程向 UI 线程产生一个可冻结的对象,需要冻结该对象。
但是,当它基于调色板时,我无法冻结图像。BitmapPalette
派生自,DispatcherObject
所以我不能冻结它。
如何将调色板图像从另一个线程生成到 WPF UI 线程?
这是示例代码:
internal static Task<BitmapSource> GetImageAsync()
{
return Task.Run<BitmapSource>(() =>
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(@"test.jpg");
bi.DecodePixelWidth = 16;
bi.EndInit();
FormatConvertedBitmap fcb = new FormatConvertedBitmap(bi, PixelFormats.Indexed2, new BitmapPalette(bi, 4), 1);
// Required for the UI thread to be able to use the bitmap.
// However, fcb.CanFreeze is false, though.
fcb.Freeze();
return fcb;
});
}
...这是我得到的警告(错误):
System.Windows.Freezable Warning:
2 : CanFreeze is returning false because a DependencyProperty
on the Freezable has a value that is a DispatcherObject
with thread affinity
@克莱门斯:
这是我想出的解决方法。它非常类似于您的解决方案。不过我省略了复制。
private void CopyBitmapSourceToUi(BitmapSource image)
{
BitmapSource uiSource;
uiSource = BitmapFrame.Create(image);
uiSource.Freeze(); // locks bitmap and enables access by UI thread
Dispatcher.Invoke(() => Source = uiSource);
Thread.Sleep(10); // WPF requires a short while to render the picture. During that period, you cannot create a WritableBitmap from the source image. So I added a minor delay.
}
但是,使用我的解决方案,当 WPF 呈现图像时,我似乎无法从源创建 WritableBitmap(请参阅上面的评论)。
解决方案
您可以创建调色板位图的副本,如下所示。
为简单起见,它使用Indexed8
代替Indexed2
,以便源像素缓冲区中的每个字节都包含一个调色板索引。
目标位图使用该Rgb24
格式,因此源缓冲区的每个字节都映射到目标缓冲区的 3 个字节。您也可以使用Bgr24
,但您必须更改 for 循环中的索引。
var palette = new BitmapPalette(bi, 4);
var fcb = new FormatConvertedBitmap(bi, PixelFormats.Indexed8, palette, 1);
var sourceBuffer = new byte[fcb.PixelWidth * fcb.PixelHeight];
var targetBuffer = new byte[3 * sourceBuffer.Length];
fcb.CopyPixels(sourceBuffer, fcb.PixelWidth, 0);
for (int i = 0; i < sourceBuffer.Length; i++)
{
var color = palette.Colors[sourceBuffer[i]];
targetBuffer[3 * i + 0] = color.R;
targetBuffer[3 * i + 1] = color.G;
targetBuffer[3 * i + 2] = color.B;
}
var bitmap = BitmapSource.Create(
fcb.PixelWidth, fcb.PixelHeight,
fcb.DpiX, fcb.DpiY,
PixelFormats.Rgb24, null,
targetBuffer, 3 * fcb.PixelWidth);
bitmap.Freeze();
推荐阅读
- linker - multiboot2 标头在 ELF 文件中“为时已晚”(偏移量很大),即使它是第一部分
- kubernetes - 不使用 CSI 将 Kubernetes PV/PVC 备份到本地磁盘?
- python - 如何在 HTML 代码中使用 Python 变量?
- python-3.x - Python & OAuth2 - 不支持或格式错误的授权标头
- sql - oracle 动态数据透视错误:SQL 命令未正确结束
- laravel - 未定义的变量:数据 laravel 8
- reactjs - 使用 map 和 fetch 时反应,意外的多个结果
- python - 如何根据计数删除嵌套列表中的列表?
- css - 如何使用 nth-child 每 n 个元素选择多个元素?
- r - 如何使用 R 将顺序编号的 id 属性分配给 XML 文件?