Most of the things I talk about on this weblog are serious posts on serious problems and solutions. However that is not the only thing I like to do with Software. From time to time I come across whaky ideas that aren’t necessarily useful.
The photo mosaic is one of those things. In this article I’m going to show you what it is and how you can build one yourself. Ooh and if you don’t want to build one, you can also download the code right at the bottom of the article.
Unfortunately this website doesn’t really work great for showing demo code, so you will have to look at some screenshots.
The idea is that the application will take any kind of picture and divide it into a preconfigured number of horizontal and vertical tiles. The control will animate the tiles when you change the picture that is displayed into another picture.
Basic structure of the mosaic control
The control is made out of a couple of components. First there’s a grid that provides a basic layout for the tiles. Inside this grid the tiles are placed that display the photo. Each tile in the grid contains its own piece of the puzzle. The outer grid is there to prevent the mosaic layout from getting deformed by bad layout of the app that the control is hosted on.
The following picture shows the schematics of the control.
Building the grid
Because you can change the number of tiles and even the size of the tiles being displayed there’s really no point in making a static control template for the photo mosaic. So instead of doing that, I just created a template with an empty grid on it. When the control is initialized I will fire up a method that will generate a grid for me.
The following snippet demonstrates this:
The first method shown in the snippet is the OnApplyTemplate method. This method is called by the runtime when the controltemplate is applied to the control. By invoking this method, the runtime allows you to link the layout of the control to the behavior of the control.
The second method shown generates a grid that contains (Rows + 2) * 2 – 1 RowDefinition objects to accomodate for the fact that there are lines between the cells. The same is done for the columns in the grid.
Each time you select a different number of tiles or change the size of the tiles, the grid will get updated with the new layout.
Reading between the tiles
No grid is complete without the gridlines. The photo mosaic sample has a pretty neat set of lines with the lines fading towards the ends. I created those by adding one tile to the end of each row and column. The brush applied to each line is made using the following settings in Blend:
For those less inclined to just poke around in blend, but instead like to work with hard figures here’s the settings:
- Gradientstop 1: Transparent (Offset = 0%)
- Gradientstop 2: White (Offset = 6%)
- Gradientstop 3: White (Offset = 94%)
- Gradientstop 4: White (Offset = 100%)
I used the same settings for horizontal and vertical lines, but with a different start and endpoint for the brush.
This makes the line fade inside the extra tile at the end of each row and column. Not a very difficult trick, but it takes a bit of fiddling in Blend to get it right.
The lines themselves are created using the following snippet of code:
There’s a ColumnSpan (to create a horizontal line) or RowSpan (to create a vertical line) value applied to each linetTo make the line go all the way horizontally or vertically.
Note: Because the lines are generated dynamically there isn’t any templatebinding applied. This can be somewhat annoying, because the line brushes aren’t updated when you change them in Visual Studio or in Expression Blend. I’ve added a bit of extra code to the dependency properties for the brushes to fix this issue.
Adding the tiles to the grid
The next step in creating the mosaic, is to create the contents of the mosaic itself. This is done by generating tiles for each column and row.
For the image to be tiled correctly I’m creating a brush with a relative transform applied to it. This causes the image to be moved over the tile by the specified translation coordinates. The translation coordinates range from 0.0 in the top-left corner to 1.1 in the bottom-right corner of each tile. By specifying a number bigger than one, I’m basically saying the image should be moved a specified number of tiles.
The following snippet demonstrates the creation of the tile brush:
Animating the tiles
The tiles in the mosaic are animated using two storyboards. One to move the tiles away from the grid and one to move them back in with the new image. Again because the grid and tiles are generated dynamically I had to create the animations in code to.
To create the animation for moving tiles away from the grid is created using the following two methods:
The first method creates the storyboard by adding animations for each tile to it. The tile animations are created using an iterator that returns a couple of animation timeline. One to move the tile horizontally, one to move the tile vertically and one to hide the tile.
To make the animation a bit more fun I added an easing function to it. The cubic ease makes the animation appear to go faster towards the end.
Once the hide animation is completed, the new image is applied and the show animation is started. To create this animation I created a new method that move the tiles into their original position.
Exactly the same animate tiles method is used here, but instead of providing a positive or negative translation value I choose a neutral translation here.
The transforms that are animated were added before hand when generating the tiles. Animating the render transforms has two advantages. The first one is that you don’t need to change the control layout for the animations to work, which means less code to write. The second advantage is that the animation can go on beyond the border of the control itself. Which makes for a prettier effect.
The demo is pretty basic, but you can of extend it by connecting to Flickr, Bing or Google to bring in more photos. Adding new photos is easy because I used an ImageSource which can be anything from a resource to an image downloaded from a webserver.
Don’t have Silverlight or looking for an alternative? Checkout Marco’s weblog: http://www.marcofolio.net/webdesign/jfancytile_a_jquery_tile_shifting_image_viewer_plugin.html for an alternative implementation using jQuery.