WPF HACKERY! PART II
Native Pointer to Bitmapsource's Buffer
Last post I covered how to get reference to the WPF DirectShow graph and inject your own filter chain. Like I stated, I think this approach is sort of dirty. I say that because it still lacks the control and flexibility needed.
So what if we could get direct access a BitmapSource's native image buffer? You know one exists, though the WPF API hides it (probably for good reason). If we could efficiently update a BitmapSource's buffer, we could do fast image processing (brightness, contrast, hue, etc) or even make our own MediaPlayer in WPF without having to even use DirectShow!
Well Friday night, after the bar, I set out with the goal of getting that BitmapSource's buffer. After about six hours and a hangover later, I got what I was looking for. Let me tell you, it was a crash course in Reflection and WIC APIs.
Let me give a short explaination on how this is done. WPF uses the unmanaged WIC in the backend for imaging, so it would make sense that WPF keeps reference somewhere to the WIC handle. At least this is what I was guessing. Poking around with Reflection, I found the WIC handle I was looking for in a private field in the BitmapSource class. I then did some research into WIC on how to lock the bitmap's pixels and get the native buffer. Plugging in all the pieces, I was able to get the pixel's pointer!
Doing some tests, writing to the buffer, I found that my xaml Image that held my hacked BitmapSource, was not changing. :(. I ran a Image.InvalidateVisual() and the change rendered!
I did some tests, writing a 640x480 at 30 times/sec resolution buffer to the BitmapSource's buffer. There were zero page faults, RAM usage didn't move at all, but the CPU usage was around 7%. This leads me to believe that I am indeed copying over the same buffer each time and the CPU usage is due to either my lack of WIC/WPF knowledge or that is the hit at which WPF rendering takes for doing such operations.
Now we can create high-performance imaging routines in WPF. We can make custom DirectShow renderers that take our native BitmapSource buffer and write to it. We can implement our own players w/o DirectShow. So many implementations!
The only caveat as of now is I seem to only be able to make this work with RGB32 pixel format.
**UPDATE: Other's have claimed to be able to use other color spaces. Play around, you might get lucky
Also DO NOT FORGET TO RUN Image.InvalidateVisual() after you update the BitmapSource's buffer or you will not see anything change!
**UPDATE: After Image.InvalidateVisual, make sure you get the pointer from the BitmapBuffer.BufferPointer property. The get{} relocks the bitmap pixels.
Here is the example usage of the class (Source for WPFUtil.BitmapBuffer class in file attached at the bottom)
int width = 640; int height = 480; int bpp = 4; //These are 'dummy' pixels //only used to create our bitmap //further editing after bitmap creation //of these pixels does nothing, that is //what this hack is for byte[] pixels = new byte[width*height*bpp]; //Create a new bitmap source BitmapSource bmpsrc = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgr32, null, pixels, width * bpp); //Set our Image in our Xaml to our //new bitmap TestImage.Source = bmpsrc; //Create our helper class buf = new WPFUtil.BitmapBuffer(bmpsrc); //Woo a pointer! IntPtr buffPointer = buf.BufferPointer;BitmapBuffer.cs - Get native pointer to BitmapSource's buffer
원본 URL : http://jmorrill.hjtcentral.com/Default.aspx?tabid=428&EntryID=16

