Question

i'm currently experimenting using PixelShaders introduced with .net 3.5 sp1 to improve image processing performance. everything is much faster , but til yet i just had effects applied to some elements in my wpf forms, that i actually want to avoid.

we have a bunch of image processing functionality and i'd like to replace some othe the stuff piece by piece with pixel shaders to gain some performance. is there a way to apply such a pixel shader to an ImageSource without having to display it?

Was it helpful?

Solution

For who still needs this: I just created this article here that shows how to do it in WPF. http://www.codeproject.com/Articles/642151/Pixel-shaders-in-a-background-thread-in-WPF

The relevant code copied below. it is from a class with some stored variables

  • Source: an ImageSource
  • DpiX, DpiY: doubles containing Dpi of source
  • img: WPF Image control
  • viewbox: WPF ViewBox control
  • WPF_DPI_X, WPF_DPI_Y: const doubles with value 96.0

1.the Image img is embedded in a Viewbox (also off-screen)

//prepare images
img = new Image();
img.Stretch = Stretch.None;
viewbox = new Viewbox();
viewbox.Stretch = Stretch.None;
viewbox.Child = img; //control to render

2.img and viewbox are sized to the correct proportions, also some layout functions are called on viewbox. this make the controls render with the shader applied.

/// <summary>
/// Loads the image and viewbox for off-screen rendering.
/// </summary>
public void LoadImage(double width, double height)
{
    img.BeginInit();
    img.Width = width;
    img.Height = height;
    img.Source = Source;
    img.EndInit();

    viewbox.Measure(new Size(img.Width, img.Height));
    viewbox.Arrange(new Rect(0, 0, img.Width, img.Height));
    viewbox.UpdateLayout();
} 

3.And to get the contents of the image, a "screenshot" if you will:

void SaveUsingEncoder(BitmapEncoder encoder, Stream stream)
{
    RenderTargetBitmap bitmap = new RenderTargetBitmap((int)(img.Width * DpiX / WPF_DPI_X), (int)(img.Height * DpiY / WPF_DPI_Y), DpiX, DpiY, PixelFormats.Pbgra32);
    bitmap.Render(viewbox);

    BitmapFrame frame = BitmapFrame.Create(bitmap);
    encoder.Frames.Add(frame);
    encoder.Save(stream);
} 

Also, if you want to run this in a separate thread you will need to create a thread with

thread.SetApartmentState(ApartmentState.STA);

for more info and a demo project, see the article.

OTHER TIPS

Seeing you did not get any answer yet from C# experts, I will try to approach this from C++ DirectX developer perspective, hoping that if my answer is not useful, at least it will point you in the right direction. I know little about C# and nothing about how PixelShaders are supported, therefore chance is am complete wrong and what I write here does not apply at all. In such case feel free to comment or down vote as needed

What is generally done in C++ / DirectX to achive this is:

Preparation (done once)

  • Create render target using CreateRenderTarget
  • Create off-screen surface using CreateOffscreenPlainSurface
  • Set render target surface using SetRenderTarget
  • Create any other input resources needed (Textures, Vertex Buffers ...)

Rendering (done multiple times)

  • Update input resources (textures, buffers) as needed
  • Render
  • Copy the contents of the render target into the off-screen surface via GetRenderTarget
  • Lock the off-screen surface and read its content on the CPU
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top