简体   繁体   中英

How can I yield a palette image from another thread to WPF UI thread?

I want to create/manipulate a palette based image asynchronously and yield that image to the WPF UI thread.

In order to yield a freezable object from another thread to the UI thread it's required to freeze the object.

However, I'm not able to freeze the image when it is palette based. The BitmapPalette derives from DispatcherObject , so I cannot freeze it.

How can I yield a palette image from another thread to WPF UI thread?


Here's the sample code:

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;
  });
}

... and here's the warning (error) I get:

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

@Clemens:

Here's the workaround I came up with. It resembles your solution very much. I omitted the copying though.

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.
}

With my solution, though, it looks like I cannot create a WritableBitmap from the source while WPF renders the image (see comment above).

You may create a copy of the paletted bitmap like shown below.

For simplicity, it uses Indexed8 instead of Indexed2 , so that each byte in the source pixel buffer contains exactly one palette index.

The target bitmap uses the Rgb24 format, so each byte of the source buffer is mapped to 3 bytes of the target buffer. You could as well use Bgr24 , but you would have to change the indexes in the for-loop.

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM