Silverlight Mandelbrot Zoomer
Written by Mike James   
Thursday, 12 August 2010
Article Index
Silverlight Mandelbrot Zoomer
Displaying the result
Using the byte array
Zooming
Final touches
Live Zoomer

The plotting

Now that we have the function that computes what we want to plot, the next part of the project is all about displaying the result and allowing the user to zoom in on sections of the plot.

 

Banner

 

The program was developed in a stepwise fashion but to save the trouble of having to keep on explaining a revised version to I'm going to present each part complete with everything need to make the whole work even if it doesn't make much sense until later.

Place button on the window and set its content to "Reset". Next place a Canvas panel on the window and an Image control with it. Set the Image control to 200 by 200. The XAML should read something like:

<UserControl x:Class="SilverZoomer.MainPage"
usual generated namespaces etc >
 <Grid x:Name="LayoutRoot"
Background="White">
   <Button Content="Reset"
Height="23"
HorizontalAlignment="Left"
Margin="12,12,0,0"
Name="button1"
VerticalAlignment="Top"
Width="75"
Click="button1_Click" />
      <Canvas  Name="canvas1"
Margin="0,41,0,-40" >
        <Image Canvas.Left="0"
Canvas.Top="0"
Height="200"
Name="image1"
Stretch="Fill"
Width="200" />
    </Canvas>
  </Grid>
</UserControl>

The key idea is that we are going to use a Rect struct to specify the area that is going to be plotted. Notice that the area to be plotted is specified using standard Cartesian, i.e. x,y, co-ordinates which correspond to a complex value by the usual z=x+iy relationship. The rectangle being plotted will have to be available to other methods so we have to declare is as a property:

private Rect area= new Rect(
new Point(-2.4, -1.5),
new Point(0.8, 1.5));

This is initialised to an area that provides a good initial view of the Mandelbrot set - found by trial and error.

The initial display can be constructed as soon as the application is loaded so we might as well put a call to the method that does the plotting, i.e. drawSet, into the control's load event handler:

private void UserControl_Loaded(
object sender, RoutedEventArgs e)
{
image1.Source = drawSet(area);
}

Now we have to write the drawSet method. First we need to create a WriteableBitmap ready to draw on. First we need to add:

using System.Windows.Media.Imaging;

and start the drawSet method:

private WriteableBitmap drawSet(Rect area)
{
int PixelHeight = (int) image1.Height;
int PixelWidth = (int) image1.Width;
WriteableBitmap wbmap = new
WriteableBitmap(PixelWidth, PixelHeight);
int BytesPerPixel =4;

In this case the size of the bitmap is determined by the size of the Image control but this can be changed if you need a different size. If you know the WPF WriteableBitmap then you will be wondering why the constructor is so much simpler in Silverlight? The reason is that the Silverlight WriteableBitmap uses a fixed ARGB pixel format.

If you need to know more about WriteableBitmaps then see: WriteableBitmap in Silverlight.

The WriteableBitmap doesn't give you easy access to its pixels in a two dimensional way - it exposes the pixels as a single linear 1D int32 array via its Pixels property. Each element of the array stores the ARGB value with the high byte as the alpha channel then the red, green and blue.

Notice that this is again different from the WPF WriteableBitmap which needs an external byte array to hold the pixels and each pixel corresponds to four bytes in the array.

To make use of the array we need to know how the pixels are stored and key in this is the concept of "stride".

The "stride" is simply the number of bytes in the array that correspond to a single "row" or horizontal line of pixels in the bitmap - which in this case is just:

int s = wbmap.PixelWidth;

Most of the difficulties of the calculation is in converting between different co-ordinate systems. In this particular situation we have three co-ordinate systems to contend with. The first is the mapping of the linear 1D array to the 2D pixels of the bitmap.

Normally in building up a plot you would create a nested pair of for loops that scanned the rows and columns taking each pixel in turn, e.g. the pixel at i,j. You would then use a storage mapping function to convert the i,j co-ordinate into the location of the pixel as stored in the array.

That is the data for pixel at i,j is stored in:

wbmap.Pixels[i+j*s]

Banner

<ASIN:0470524650 >

<ASIN:1430230185 >

<ASIN:0672333368 >



Last Updated ( Monday, 30 October 2023 )