Saturday, August 22, 2009

Silverlight – SpriteSheet management with WriteableBitmap

As part of my Silverlight education process I have been writing some games. First a few basic things like Sildoku and Peg Solitaire. Now I am advancing to something a bit more challenging, I am writting a small 2d racing game. For this I started with some of the fun parts, writing a Sprite manager, while this is still in the early stages I thought I would share one of my basic classes the SpriteSheet class.
For those of you who do not know what a sprite sheet is, it is an set of images placed in a grid layout and composed into one larger image. Often sequential images in the grid will represent frames in an animation of some action such as walking etc. Take a look at this Wikipedia link.
My sprite manager at this stage supports both static and animated sprites, using one or more sprite sheets as a source of the images. Here is the code for the SpriteSheet class.
public class SpriteSheet
{
  private BitmapSource _spriteSheetSource;
  private WriteableBitmap _spriteSheetBitmap;
  private int _sheetWidth;
  private int _sheetHeight;    

  public SpriteSheet(BitmapSource spriteSheetSource)
  {
    if (spriteSheetSource == null) throw new ArgumentNullException("spriteSheetSource");

    _spriteSheetSource = spriteSheetSource;
    _spriteSheetBitmap = new WriteableBitmap(_spriteSheetSource);
    _sheetWidth = _spriteSheetBitmap.PixelWidth;
    _sheetHeight = _spriteSheetBitmap.PixelHeight;
  }

  public WriteableBitmap GetBitmap(int x, int y, int width, int height)
  {
    WriteableBitmap destination = new WriteableBitmap(width, height);      
    GetBitmap(destination, x, y, width, height);
    return destination;
  }

  public void GetBitmap(WriteableBitmap targetBitmap, int x, int y, int width, int height)
  {
    // Validate incomming data
    if (targetBitmap == null) throw new ArgumentNullException("targetBitmap");
    if (x < 0 || x >= _sheetWidth) throw new ArgumentOutOfRangeException("x");
    if (y < 0 || y >= _sheetHeight) throw new ArgumentOutOfRangeException("y");
    if (width < 0 || (x + width > _sheetWidth)) throw new ArgumentOutOfRangeException("width");
    if (height < 0 || (y + height > _sheetHeight)) throw new ArgumentOutOfRangeException("height");

    // Get pixel buffers for the sprite sheet and the target bitmap
    int[] sourcePixels = _spriteSheetBitmap.Pixels;
    int[] targetPixels = targetBitmap.Pixels;

    // Calculate starting offsets into the pixel buffers      
    int sourceOffset = x + (y * _sheetWidth);      
    int targetOffset = 0;

    // Note that the offsets and widths are multiplied by 4, this is because Buffer.BlockCopy requires
    // byte offsets into the buffers and our buffers are integer buffers. To optimize this I have 
    // premultiplied to values so that the multiplication is removed from the loop
    int sourceByteOffset = sourceOffset << 2;
    int sheetByteWidth = _sheetWidth << 2;
    int targetByteWidth = width << 2;
    for (int row = 0; row < height; ++row)
    {
      Buffer.BlockCopy(sourcePixels, sourceByteOffset, targetPixels, targetOffset, targetByteWidth);
      sourceByteOffset += sheetByteWidth;
      targetOffset += targetByteWidth;
    }      
  }    

  public int Width
  {
    get { return _sheetWidth; }
  }

  public int Height
  {
    get { return _sheetHeight; }
  }
}
The interesting function in this class is the GetBitmap(WriteableBitmap targetBitmap, int x, int y, int width, int height) overload. This function is the work horse of the class. It is responsible for transferring a region of the source image starting at x, y to the new WriteableBitmap. The GetBitmap(int x, int y, int width, int height) overload calls this function with a pre-allocated WriteableBitmap of the correct dimensions.
Using the class is quite simple. Assume you have a couple of standard Image controls declared on your page, something like the following.

<UserControl x:Class="Vulcan.SL.TestApplication.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  <Canvas x:Name="LayoutRoot">  
    <Image x:Name="mySprite1" Width="100" Height="100" Canvas.Left="0"/>
    <Image x:Name="mySprite2" Width="100" Height="100" Canvas.Left="110"/>
  </Canvas>
</UserControl>
You will notice that I have not set the Source of the images, we will do this in code using the GetBitmap function of the SpriteSheet class.
Next in the code behind file we need to load the image that and create an instance of the SpriteSheet class passing the image. The trick here is that you should only create the SpriteSheet instance once the image has been fully downloaded. Here is one way of doing this.
public MainPage()
{
  InitializeComponent();

  BitmapImage spriteSheetBitmap = new BitmapImage(new Uri("/Images/WindmillLeft.png", UriKind.Relative));      
  spriteSheetBitmap.CreateOptions = BitmapCreateOptions.None;
  spriteSheetBitmap.ImageOpened += new EventHandler<RoutedEventArgs>(image_ImageOpened);       
}

void image_ImageOpened(object sender, RoutedEventArgs e)
{
  BitmapImage spriteSheetBitmap = sender as BitmapImage;

  SpriteSheet spriteSheet = new SpriteSheet(spriteSheetBitmap);

  // Set the source of the mySprite1 to an image extracted from the SpriteSheet
  mySprite1.Source = spriteSheet.GetBitmap(0, 0, 224, 240);

  // Set the source of the mySprite2 to an image extracted from the SpriteSheet
  mySprite2.Source = spriteSheet.GetBitmap(2240, 0, 224, 240);
}
Here I create a BitmapImage class and set the CreateOptions to BitmapCreateOptions.None. This ensures that the image is created without delay, however the creation still takes place asynchronously so we need attach a handler to the ImageOpened event which indicates that the image has been downloaded and decoded, indicating that we can now use the image.
In the ImageOpened event handler we can construct our SpriteSheet and used the GetBitmap function to extract the regions of interest and assign them to the relevant Image instances. I have purposely not used instance members in these examples so that each piece of code is self contained, and of course I have forgone error checks etc. for the sake of clarity.
Note that the SpriteSheet does not perform any kind of caching of the regions that are extracted, this means that if you request the same region multiple times with the GetBitmap function you will receive new instances of WriteableBitmaps for each call. In my case I take care of the caching at a higher level. I have a SpriteFrameSet class which is a cache of the frames used for an animation sequence, internally this class uses the SpriteSheet to build an array of images ie. frames, and from that point on the array is referenced to retrieve each frame from the frame set.
As my code for the Sprite and AnimatedSprite classes matures I will hopefully find some time to release that on this blog.

Wednesday, August 19, 2009

Silverlight – My first Silverlight games published

To day is somewhat of a milestone for me, after more than 15 years in the industry I have never released anything I have written into the wild. More than that, I do very little UI work, focusing mainly on backend server components.

So what is all my excitement about, well I have written 2 small Silverlight games and submitted them to Silver Arcade. Neither game is really ground breaking, they have served as an avenue gaining some basic experience with Silverlight. Each game was written in less than 24 hours so there are no fancy bells and whistles, heck they do not even have sound (Mostly because I do not even have speakers attached to my computer, no I do not play games :))

The first game is called Sildoku, yes you guessed it, it is a Sudoku clone. It has 4 levels of difficulty and each game is randomly generated on the fly. Try it here

ScreenshotSmall 

The second game is Peg Solitaire. Which you can try here

Promotional

Tuesday, August 18, 2009

Archive – Hacking my way across the process boundary

This is another one of the posts that I am migrating from my previous blog. The post is based on an answer I provided on a news group post. A subsequent post provides a similar solution for .NET. This post was put up in early 2004.

About 2 years ago a C++/MFC question appeared on the news groups asking how to send windows messages to a ListView control in another process and receive the data returned in the LVITEM structure. At the time I came up with what I thought to be an imaginative solution and it worked for the op. Recently this question reappeared but this time for the .NET environment, a quick Google search and I pointed the OP to the original post, leaving the conversion to .NET as an exercise for the reader. Since the routine did the job again and I remember having some fun playing around with the routine so I thought I would revive it here. Now I just need to get around to converting it to .NET :)

In short you allocate a block of memory in the target process using VirtualAllocEx, this block must be big enough to hold both the LVITEM structure and any string data you expect back. You also declare an instance of the structure in your local process and setup all the necessary fields; the only field that is configured differently is the pszText member, which is defined as pointing to the block of memory in the target process offset by the sizeof the LVITEM structure. Then the local structure is moved to the target process using WriteProcessMemory. Once all the structures are in place and in the correct process all that remains is to send the message using SendMessage, and passing the address of the structure in the remote process as the lParam. On successful return of the SendMessage the remote process should contain the requested data and you can copy the data back to the local process using ReadProcessMemory. Once again the pszText pointer has to be fixed up in the local process to reference the local buffer offset again by the sizeof LVITEM.

The following C++ code provides an example of getting a item from a ListView hosted in another process.

const DWORD dwBufSize = 1024;

DWORD dwProcessID;
DWORD dwResult;
HANDLE hProcess;

BYTE *lpRemoteBuffer;

LVITEM lvItem = {0};

BYTE lpLocalBuffer[dwBufSize] = {0};

// Get the process id owning the window
::GetWindowThreadProcessId( hwndListView, &dwProcessID );

// Open the process wih all access (You may not have the rights to do this)
hProcess = ::OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcessID );

// Allocate a buffer in the remote process
lpRemoteBuffer = (BYTE*)::VirtualAllocEx( hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE );

// Fill in the LVITEM struct, this is in your own process
// Set the pszText member to somewhere in the remote buffer,
// For the example I used the address imediately following the LVITEM stuct
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.cchTextMax = 50;

// Point to after LVITEM in the remote buffer
lvItem.pszText = (LPTSTR)(lpRemoteBuffer + sizeof( LVITEM ));

// Copy the local LVITEM to the remote buffer
::WriteProcessMemory( hProcess, (LPVOID)lpRemoteBuffer,
&lvItem, sizeof(LVITEM), NULL );

// Send the message
::SendMessage( hwndListView, LVM_GETITEM, 0, (LPARAM)lpRemoteBuffer);

// Read the struct back from the remote process into local buffer
::ReadProcessMemory( hProcess, (LPVOID)lpRemoteBuffer, lpLocalBuffer,
dwBufSize, NULL );

//Fix pszText to point to same offset in local buffer
lvItem.pszText = (LPTSTR)(lpLocalBuffer + sizeof( LVITEM ));

// Clean-up
::VirtualFreeEx( hProcess, (LPVOID)lpRemoteBuffer, 0, MEM_RELEASE );
::CloseHandle( hProcess );

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?

Friday, August 14, 2009

Silverlight – Set focus to the silverlight control

When loading a web page which hosts a Silverlight application, I noticed that before interacting with the Silverlight application the user was required to first click on the application so that it got input focus. It was immediately obvious that I would need to use a small piece of java script to set the focus to the control. This is how I got it to work.

1. Give the <object> element in the web page an ID
2. Pass the ‘onLoad’ parameter on the Silverlight application assigning a JavaScript function that will focus to application in the browser.

<object id="silverlightControl" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/DragSnapDemo.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="onLoad" value="silverlightControlHost_Load" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>


3. Finally write the JavaScript function that is called when the Silverlight application has be loaded



function silverlightControlHost_Load(sender, args) 
{
var control = document.getElementById("silverlightControl");
control.focus();
}


The trick here is that the JavaScript function is only called once the Silverlight application has been fully downloaded and loaded into the browser. To achieve this I used the onLoad event of the application.