Saturday, August 15, 2009

Silverlight – WriteableBitmap SetPixel extension method

I am in the process of writing two interesting controls (At least interesting to me). For one of the controls I needed a way to directly set pixels on the WriteableBitmap with full support for alpha blending, the result is the following extension method.

using System;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ExtensionMethods
{
public static class WriteableBitmapExtensions
{
public static void SetPixel(this WriteableBitmap wb, int x, int y, Color color)
{
// Validate that the x,y coordinates are within the bounds of the bitmap
if (x < 0 || x >= wb.PixelWidth || y < 0 || y >= wb.PixelHeight) return;

int offset = (y * wb.PixelWidth) + x;
int pixel;
int[] buffer = wb.Pixels;

if (color.A == 255)
{
// Since no alpha blending is required we can directly use the incomming color
// compose the integer that needs to be written to the pixel buffer.
pixel = (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
}
else
{
// Get the current pixle in the pixel buffer that we need to blend with
pixel = buffer[offset];

// calculate the alpha channel ratios used for the blend of
// the source and destination pixels
double sourceAlpha = color.A / 255.0;
double inverseSourceAlpha = 1 - sourceAlpha;

// Extract the color components of the current pixel in the buffer
byte destA = (byte)(pixel >> 24);
byte destR = (byte)(pixel >> 16);
byte destG = (byte)(pixel >> 8);
byte destB = (byte)pixel;

// Calculate the color components of the new pixel.
// This is the blend of the destination pixel and the new source pixel
byte pixelA = (byte)((color.A * sourceAlpha) + (inverseSourceAlpha * destA));
byte pixelR = (byte)((color.R * sourceAlpha) + (inverseSourceAlpha * destR));
byte pixelG = (byte)((color.G * sourceAlpha) + (inverseSourceAlpha * destG));
byte pixelB = (byte)((color.B * sourceAlpha) + (inverseSourceAlpha * destB));

// Reconstitute the color components into an int to be written to the pixel buffer
pixel = (pixelA << 24) | (pixelR << 16) | (pixelG << 8) | pixelB;
}

// Write new pixel to the pixel buffer
buffer[offset] = pixel;
}
}
}


If you are interested, the 2 controls I have written are




  1. TextureSurface – A control which supports direct manipulation of the pixel buffer. The convenience is that there is no need for the developer to manage the WriteableBitmap and the Image control and there are precooked functions for setting pixels with alpha channel support etc.


  2. ImageEx – This is an enhanced Image control which supports rendering only a portion of the source image, the purpose is to provide a means of rendering regions from a source image to be used like a sprite sheet.



Both controls are quite functional, but being quite new to Silverlight and not having done any WPF I am a little concerned that I have not made the most efficient use of the Control life cycle and feel like I have hacked a few things to work. Anyone out there willing to take a look at the code and make a few recommendations?

2 comments:

  1. Based on the maturity of the code in your extension method, looks like you're off to a good start. Why not post a download of the controls code. People will play with it and make suggestions.

    ReplyDelete
  2. also see http://writeablebitmapex.codeplex.com/

    ReplyDelete