The WriteableBitmap is one of the most interesting new features in Silverlight 3, especially for game development. I’m looking forward to the first pixel perfect collision game using this technology, one of my favorites was the classic Lemmings game.
The Pixels array contains one integer for every pixel, and you can find the correct array element by indexing row * width + col like this:
WriteableBitmap bm = new WriteableBitmap(width, height); bm.Pixels[row * bm.PixelWidth + col] = value;
Personally I would have liked to have seen the Pixels array be made up of unsigned integers, since all bits are used and it would have made using hex values easier. The bytes in the 4 byte integer value correspond to alpha, red, green, and blue respectively. So pretty straightforward, a SetPixel method should look like this:
void SetPixel(WriteableBitmap bm, int row, int col, byte alpha,
byte r, byte g, byte b)
{
int idx = row * bm.PixelWidth + col;
bm.Pixels[idx] = (alpha << 24) | (r << 16) | (g << 8) | b;
}
Unfortunately there is a problem with this. This would be fine for ARGB32 format, but what the Pixels array uses is Premultiplied ARGB32. This means that if you specify an alpha value other than 255 (fully opaque), the other 3 values (red, green, and blue) need to be scaled based on the alpha value. A fixed version could look something like this:
void SetPixel(WriteableBitmap bm, int row, int col, byte alpha,
byte r, byte g, byte b)
{
int idx = row * bm.PixelWidth + col;
byte r1 = (byte)(r * (alpha / 255d));
byte g1 = (byte)(g * (alpha / 255d));
byte b1 = (byte)(b * (alpha / 255d));
bm.Pixels[idx] = (alpha << 24) | (r1 << 16) | (g1 << 8) | b1;
}
I assume that this format is more easily consumable for display. I spent a few hours figuring this out so hopefully you won’t have to struggle with it.
July 16th, 2009 at 1:41 AM
Thanks for sharing this finding. I think I can use it for making the background black-and-white while showing a child window.
July 16th, 2009 at 2:15 AM
Thanks for sharing this great info.
July 16th, 2009 at 3:08 AM
WriteableBitmap pixel format in Silverlight 3 | Silverlight Games 101…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
July 16th, 2009 at 2:53 PM
You probably should change:
void SetPixel(WriteableBitmap b, int row, int col, byte alpha,
byte r, byte g, byte b)
to:
void SetPixel(WriteableBitmap bm, int row, int col, byte alpha,
byte r, byte g, byte b)
Otherwise, THANKS!!!
July 16th, 2009 at 4:07 PM
Fallon,
Thanks yeah I guess I should have tested the code, I usually do but didn’t this time. The samples have been updated based on your suggestion.
Bill
July 17th, 2009 at 1:13 AM
Neat article! Just one thing: maybe I’m too old school, those divisions look scary to me. Perhaps we can have one “alpha / 255d” instead of three (though I’m not too sure if that is going to make any difference in 2009)…
Regards,
Ben.
July 17th, 2009 at 4:30 AM
I used Bill’s methods, optimized them a bit and packed them into a static class as extension methods, which is downloadable here http://kodierer.blogspot.com/2009/07/writeablebitmap-extension-methods.html
You can also find a code snippet on how to use it there.
July 21st, 2009 at 1:59 PM
Found it on other forum, when researching bad performance :
do not use WriteableBitmap.PixelWidth/PixelHeight property in pixel rendering cycle, it cost too much time.
// set _pwidth=bitmap.PixelWidth in constructor
public void SetPixel(int x, int y, int color)
{
var index = (_pwidth * (y – 1)) + x;
frontBuffer[index] = color;
}
Mog Liang
Microsoft Online Community Support
September 3rd, 2009 at 1:37 PM
Excellent site, keep up the good work
September 19th, 2009 at 8:14 PM
Thank you so so much for saving my life! I spent last 2 hours wondering what I was doing wrong, because I was SURE it was using ARGB (otherwise it would be explained in samples or in the doc, right?).