Thursday, July 16, 2009

WriteableBitmap Extension Methods

Bill Reiss (@billreiss) wrote a good blog post about the pixel format of the Silverlight 3 WriteableBitmap class and included some nice helper methods for the pixel manipulation. I used his two methods, optimized them a bit and packed them into a static class as extension methods of the WriteableBitmap. I also added some methods, which take the System.Windows.Media.Color structure as input parameter instead of bytes.
The SetPixeli overloads of the methods  use the precalculated index and don't calculate the index position itself in every call and are therefore faster.


The signature of the methods
SetPixeli(this WriteableBitmap bmp, int index, byte r, byte g, byte b);
SetPixel(this WriteableBitmap bmp, int x, int y, byte r, byte g, byte b);

SetPixeli(this WriteableBitmap bmp, int index, byte a, byte r, byte g, byte b);
SetPixel(this WriteableBitmap bmp, int x, int y, byte a, byte r, byte g, byte b);

SetPixeli(this WriteableBitmap bmp, int index, Color color);
SetPixel(this WriteableBitmap bmp, int x, int y, Color color);

SetPixeli(this WriteableBitmap bmp, int index, byte a, Color color);
SetPixel(this WriteableBitmap bmp, int x, int y, byte a, Color color);  

Usage
int index = 0;
for (int y = 0; y < writeableBmp.PixelHeight; y++)
{
   for (int x = 0; x < writeableBmp.PixelWidth; x++)            
   {
      byte alpha = (byte)(x * x + y * y);
      writeableBmp.SetPixeli(index++, alpha, Colors.Black);
   }
}
writeableBmp.Invalidate();
The index position isn't calculated with x * writeableBmp.PixelWidth + y in every iteration, instead I use an extra index variable. The incrementation is a lightweight computational operation compared to the multiplication + addition.
This short code snippet produces the image you can see above. It's really simple algorithmic beauty from the alpha channel.

Source code
You can download the complete Visual Studio 2008 solution from here. Check out my Codeplex project WriteableBitmapEx for an up to date version of the extension methods.

7 comments:

  1. Thank you.
    Can you please post a link to the source of full solution as well?
    Also, which method is preferred for drawing images fast like in games and emulators , this one or with imgSource.SetSource ?

    ReplyDelete
  2. Hello John.

    I won't be able to post the solution this weekend, but I could upload it on Sunday evening or Monday. The implementation is actually quite simple. Just use a Silverlight System.Windows.Controls.Image and set the WriteableBitmap as the Source of the image, then execute the code snippet from above.

    The WriteableBitmap is quite fast and could replace any custom Stream implementation, which were used before Silverlight 3. I plan to update my Perlin Noise example to Silverlight's 3 WriteableBitmap. You can find the Silverlight 2 implementation, which uses Balder's custom RawPngBufferStream as ImageSource, here: http://kodierer.blogspot.com/2009/05/oscar-algorithm-silverlight-real-time.html
    After I have updated it to Silverlight 3 I will write a blog post with a speed comparison.

    ReplyDelete
  3. Thanks for this!

    But I have one suggestion:

    When it tries to blit outside the boundaries, it runs into an exception. Could you possibly add some boundary checking to this so it doesn't try to blit where it can't?

    ReplyDelete
  4. I left the boundary checks out for better performance and I'm not planning to change that behavior. The caller is responsible for checking it. I will soon put the extensions up on Codeplex and you will able to suggest it there.

    ReplyDelete
  5. Hi,

    Thanks for the extensions. I tried it with a JPG image and used Colors.Black but my image did not go black. Is this code to create an image only? Can it be used to manipulate an image "loaded" into a WriteableBitmap?

    Thanks

    ReplyDelete
  6. Ok I figured out what to do. I was setting the color of the image in the ImageOpened event handler and getting a reference to the Image UI element via the sender parameter. However when I created a WriteableBitmap from the Image and used the extension method on it I had to set the Source property of the original Image UI Element to the newly created WriteableBitmap for it to work.

    ReplyDelete
  7. Yes, you have to set the Image's element source property to the new instance, otherwise it's referencing the old instance.

    ReplyDelete