Creating a 360 Degree Panorama in Silverlight

360 Degree Panorama photos are often done using QuickTimes VR or Flash to give a 360 perspective. However they are just as easy to create in Silverlight meaning you can embed them in to your application to enhance maps and create virtual tours.

Install Microsoft Silverlight


The first step is to create you cylindrical panorama image. This is a single image that represents a 360 view. These can be created with a standard digital camera, a number of photos and some stitching software to put them all together. Hugin Panorama photo stitcher is a cross platform open source photo stitching application that will allow you to easily stitch together your final image although there are many others available.

Once you have your finished image it should look something like this:

Creative Commons 360 Image

Full 360 Degree Cylindrical Panorama of London

* Note: This image is a creative commons image I am using for the sample and not taken by myself.

Next split the image into two separate images of equal size you using a photo editing software and add to your silverlight project. This is essential as the code behind will scroll the two images to the left. Then as one of the images falls off the left hand side of the canvas it is placed to the far right ready to scroll into view again. This action creates a seamless scrolling 360 degree view.

In the XAML place the two images on a canvas side by side. For this to work you must ensure that the canvas is no wider than one of the image halves. You must also clip the canvas to the same size to prevent the user from seeing the images as they scroll off the edge.

<Canvas x:Name="Frame" Width="400" Height="200">

     <Canvas.Clip>
                <RectangleGeometry Rect="0, 0, 400, 200"/>
      </Canvas.Clip>
 
        <Image x:Name="tile2" Source="Images/LeftImage.jpg"
 Stretch="None" Canvas.Left="0" Canvas.Top="0"  />     

        <Image x:Name="tile1" Source="Images/RightImage.jpg" 
Stretch="None" Canvas.Left="400" Canvas.Top="0"/>

      </Canvas>

The animation and calculations to move the images into a loop is done using the CompositionTarget.Rendering event. This is fired each time the screen updates (so speed will depend on the fps). This method is used instead of storyboard animations so that we can reset the images positions when necessary.

public MainPage()
        {
            InitializeComponent();
            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
        }

private void CompositionTarget_Rendering(object sender, EventArgs e) {
       
            if (_direction== Directions.Left)
            {
                if ((Canvas.GetLeft(tile1) + tile1.ActualWidth) < 0)
                {
                   //If tile1 fallen off the left
                    Canvas.SetLeft(tile1, Canvas.GetLeft(tile2) + tile2.ActualWidth);
                }
                else if ((Canvas.GetLeft(tile2) + tile2.ActualWidth) < 0)
                {
                    //If tile2 fallen off the left
                    Canvas.SetLeft(tile2, Canvas.GetLeft(tile1) + tile1.ActualWidth);
                }

                //Move the tiles along left
                Canvas.SetLeft(tile1, Canvas.GetLeft(tile1) - _speed);
                Canvas.SetLeft(tile2, Canvas.GetLeft(tile2) - _speed);

            } else if (_direction== Directions.Right) {

                if (Canvas.GetLeft(tile1) > Frame.ActualWidth )
                {
                    //If tile1 fallen off the right
                    Canvas.SetLeft(tile1, Canvas.GetLeft(tile2) - tile1.ActualWidth);
                }
                else if (Canvas.GetLeft(tile2) > Frame.ActualWidth)
                {
                    //If tile2 fallen off the right
                    Canvas.SetLeft(tile2, Canvas.GetLeft(tile1) - tile2.ActualWidth);
                }
             
                //Move the tiles along right
                Canvas.SetLeft(tile1, Canvas.GetLeft(tile1) + _speed);
                Canvas.SetLeft(tile2, Canvas.GetLeft(tile2) + _speed);   
            }
        }

Finally three buttons set the _direction value which sets how the animation is shown giving us our finished sample. Note that as the image scrolls left the user is given the impression of looking right. Therefore the button with the left arrow should set the _direction to Right and visa versa.

Download 360 Viewer Code

Tags: , , ,

Monday, August 31st, 2009 General 7 Comments

How to Crop instead of Clip an Image in Silverlight

The silverlight image element has a Clip property which can be used to clip an image, hiding all but the clipped area.

   <Image Source="Image.jpg" Stretch="None">
        <Image.Clip>
               <RectangleGeometry Rect="70,70,50,50"/>
       </Image.Clip>
   </Image> 

However this works as a mask rather than a crop, leaving the image element at the original size just covering over the unclipped area. For some applications this may what you need but if the image element is in a stack panel (as in the sample below) it would mean the image is surrounded by white space.

Install Microsoft Silverlight


The solution is to use a smaller Writeable Bitmap to copy the image into. In your code create a Writeable Bitmap object the size of the final cropped area (in this case 50×50). Then use the Render method to copy the Image element on to the Writeable Bitmap. When calling the Render method you will have to pass in a translate transform to move the area of the image you wish to crop in to the top left corner. Finally set the source of your image to the new Writeable Bitmap.

  //Create a bitmap of the cropped size
  WriteableBitmap wb = new WriteableBitmap(50, 50 );

  //Create a transform to move the Image to the top left
  TranslateTransform t = new TranslateTransform();
  t.X = -70;
  t.Y = -70;

  //Draw to the Writeable Bitmap
  wb.Render(sampleImage,t);
  wb.Invalidate();
                         
   //Finally set the Image back
   sampleImage.Source = wb;

Download Sample Source Code

Update: As highlighted on this stack overflow question asked by frogbot. If your image has not been added to the canvas you must ensure it has fully loaded before cropping it. This can be done via the ImageOpened event as suggested by keithmahoney.

Tags: , ,

Saturday, August 22nd, 2009 General 5 Comments

Rendering XAML to a JPEG using Silverlight 3

Something that had been relatively easily do in WPF that was near impossible in silverlight was to easily take the XAML that had been rendered on the screen and allow the user to save it as an image file.

With the release of version 3 of silverlight this is now possible through the use of the WriteableBitmap class. Once the XAML has been rendered to the to the writablebitmap it can then be written to a Bitmap file or using a third party code encoded as a JPEG or PNG as shown below. This can have a range of applications form allowing users create avitars through to saving an image of a silverlight game. Once you have the image it can then be saved to isolated storage, a web server via a web service or to the users own machine.

Install Microsoft Silverlight

In the sample I have used the FJcore library to encode it as a JPEG to keep the file small, however it is just as easy to use the Joe Stegman’s PNG encoder.

The first step is to write your XAML to the writeable bitmap which can easily be done in one line through the constructor. To render the image identical to the screen we pass null into the transform argument.

WriteableBitmap bitmap = new WriteableBitmap(sourceElement, null);

Using a type of panel such as a canvas or grid will allow you can render multiple elements together in one image. One thing to note is that it ignores the Background property of the pannel placing all elements on default bitmap black background. To get around this you can first drawn a white rectangle as a background to your image.

The easiest way to save the image and get the best mix of file size and quality is to use the FjCore Library  which is design to be a simple lightweight JPEG encoder/decoder for use with silverlight and distributed under the MIT License licence.

The fjcore encode method expect a byte array of the image so the only additional method in the project converts the WriteableBitmap (using a variation on code provided by RHLopez in a stack overflow question) and adds it to a file stream which in the case of this sample is created from the save dialog class so is inturn saved to the users machine.

private static void SaveToFile(WriteableBitmap bitmap,Stream fs)
{
            //Convert the Image to pass into FJCore
            int width = bitmap.PixelWidth;
            int height = bitmap.PixelHeight;
            int bands = 3;

            byte[][,] raster = new byte[bands][,]; 

            for (int i = 0; i < bands; i++)
            {
                raster[i] = new byte[width, height];
            }

            for (int row = 0; row < height; row++)
            {
                for (int column = 0; column < width; column++)
                {
                    int pixel = bitmap.Pixels[width * row + column];
                    raster[0][column, row] = (byte)(pixel >> 16);
                    raster[1][column, row] = (byte)(pixel >> 8);
                    raster[2][column, row] = (byte)pixel;
                }
            }

            ColorModel model = new ColorModel {colorspace = ColorSpace.RGB };

            FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);

            //Encode the Image as a JPEG
            MemoryStream stream = new MemoryStream();
            FluxJpeg.Core.Encoder.JpegEncoder encoder = new FluxJpeg.Core.Encoder.JpegEncoder(img, 100, stream);

            encoder.Encode();

            //Move back to the start of the stream
            stream.Seek(0, SeekOrigin.Begin);
           
            //Get the Bytes and write them to the stream
            byte[] binaryData = new Byte[stream.Length];
            long bytesRead = stream.Read(binaryData, 0, (int)stream.Length);

            fs.Write(binaryData, 0, binaryData.Length ); 

        }

The full code sample can be downloaded here

Tags: , , ,

Tuesday, July 21st, 2009 General 17 Comments

The use of PART_ in Control Templates

One of the most useful features of WPF is the ability to completely redesign the look of a control such a button, progress bar or slider bar relatively easily through the use of control templates. Meaning a progress bar can become round, a slider bar 3D or some other shape.

With the more complex controls (such as the progress bar) you can also keep the functionality of the original control and save having to re-write the base logic. This is done by specifying that elements in your design are the same as part of the original control. The link is created through simply naming your elements by set names usually prefixed with PART_.

In the example below we have a ControlTemplate for a ProgressBar. In order for it to keep it’s function as a progress bar the I have named the elements “PART_Track” and “PART_Indicator” to link to the two parts of original functionality. In the case of a progress bar without this when the value property is set on the progresses bar it would not be reflected in the redesigned control.

        <ControlTemplate x:Key="DarkBar" TargetType="{x:Type ProgressBar}">
            <Grid>
                <Border Name="PART_Track" CornerRadius="10" BorderThickness="2" BorderBrush="DarkGray"/>
                <Border Name="PART_Indicator" CornerRadius="10" Background="Gray" HorizontalAlignment="Left" />
            </Grid>
        </ControlTemplate>

For each control type the names that you use to specify the link are different, although they are all prefixed with the word PART_ . These is no full list online however as these are specified as TemplatePartAttributeattributes on the controls it means that they are relatively easy to discover.

Using reflection (code sample below) it is possible to get a full list.

Dim extAssembly As System.Reflection.Assembly = System.Reflection.Assembly.LoadFile("C:Program FilesReference AssembliesMicrosoftFrameworkv3.0PresentationFramework.dll")
For Each t As System.Type In extAssembly.GetTypes
For Each att As System.Windows.TemplatePartAttribute In t.GetCustomAttributes(GetType(System.Windows.TemplatePartAttribute), True)
Console.WriteLine(t.ToString & " : " & att.Name.ToString & " Type of " & att.Type.ToString)

Next
Next

Alternatively and often more practical is to use Red Gates Reflector tool. With this free application you can search through and look at the source code of the controls. The part will be specified at the top along with the other attributes as shown below.

Reflector Progress Bar Attribute Example

This has been a quick overview and further reading is often need to discover just how to use the parts as they differ per control however hopefully you can start to see how useful they are when redesigning controls.

Tags: , ,

Sunday, February 8th, 2009 General 4 Comments

XAML for WPF CheatSheet

The new version of the XAML for WPF Cheat Sheet 1.0 is now ready.

It is only one page so has been limited to the following topics:

  • Data Binding
  • Styles and Triggers
  • Resources
  • Layout
  • Transforms
  • Brushes

Download Here

Tags: ,

Tuesday, February 3rd, 2009 General 5 Comments

XAML for WPF CheatSheet 1.0 (Draft)

I have created a first draft of the XAML for WPF Cheatsheet. Although the emphasis is on WPF most parts should also work with silverlight.

I have tried to keep it to short to one page so I could only fit in the following topics:

  • Data Binding
  • Styles and Triggers
  • Resources
  • Layout
  • Transforms

I decided to miss out:

  • Complete Code Samples
  • Control Templates (with PART_ List)
  • Resource Dictionaries
  • XML Namespaces
  • Shapes
  • Controls

These could make a second page or replace current information.  It is only a draft and I would like your feedback.

Is there information that it misses? does it include something you would never use? are the descriptions too detailed? or is it perfect?

Thanks in advanced and hope it is of use.

Download Cheatsheet (link removed as been replaced by a the final version)

Tags: ,

Sunday, February 1st, 2009 General 1 Comment

Welcome

This is just a quick informal welcome to my new blog. Hopefully over time it will fill up with many useful tutorials and links as well as a portfolio of my work.

I have been a programmer for many years now this blog is a chance to share some those experiences.

Currently working on a WPF cheat sheet which should be posted soon.

Wednesday, January 28th, 2009 General 2 Comments