More Fun with Silverlight 3's WriteableBitmap
I'm currently preparing a couple of new talks for TechEd Europe, which takes place next month in Berlin. One of the talks is entitled "Cool Graphics, Hot Code: Ten Visual Effects to Make You the Envy of Your Peers." In it, I plan to present techniques for producing eye-popping graphics and visual effects in Silverlight 3. I've been writing new code samples and revising existing ones. And thanks to Silverlight 3's WriteableBitmap class, I was able to make a dramatic improvement to a sample that I originally wrote for Silverlight 2 (and before that, for Silverlight 1.0).
The sample I'm referring to is the Magnifier demo that lets you drag a virtual magnifying glass around a scene:
This demo works by creating a shadow copy of the scene that's 4 times wider and 4 times higher than the original. The shadow copy is normally invisible, but when the left mouse button goes down, a portion of the shadow copy is revealed to the user through a circular clipping region, as pictured below. The only real trick is making sure that what's shown in the clipping region maps to what's under the cursor in the foreground scene. I accomplish that by moving the shadow copy with each mouse move so that the cursor's logical coordinates in both scenes are always exactly the same.
In the Silverlight 2 version of this sample, I had to declare, in XAML, two identical copies of the scene--one to represent the scene in the foreground (the one that the user sees), and another to represent the shadow copy. I wanted to generate the shadow copy programmatically, but Silverlight 2 gave me no facility for doing that.
Enter Silverlight 3 and WriteableBitmap, which provides an elegant solution to the problem of generating the shadow copy. In the revised Magnifier demo, I use WriteableBitmap.Render with a ScaleTransform to generate a 4x image from the foreground scene:
// Render the main Canvas into the image at 4 times its nominal size
WriteableBitmap bitmap = new WriteableBitmap((int)_image.Width, (int)_image.Height);
ScaleTransform transform = new ScaleTransform();
transform.ScaleX = transform.ScaleY = 4.0;
It's now this image that the user sees through the magnifying glass rather than a duplicate Canvas. The benefit is obvious: if I want to change something in the scene, I no longer have to make the change in two places. You can judge the quality of the effect yourself by running the app online. And you can download the source code to see how it works.
If I have time before TechEd, I might factor out the magnifying glass into a custom control that can be applied to any scene (or part of a scene). Or perhaps the magnifying glass should be a behavior rather than a control. That's something to think about as I continue building code samples for the talk.
UPDATE: I just finished converting the magnifier into a custom behavior named MagnifierBehavior. Using it is now as simple as adding a reference to WintellectBehaviors.dll to your project and then adding a MagnifierBehavior to a Canvas. Thereafter, you can click anywhere in the Canvas to display the magnifying glass and everything on the Canvas is magnified through the lens. Moreover, the behavior exposes properties named ZoomFactor and LensRadius that you can use to customize the look:
<Canvas Width="340" Height="340" Background="White">
<local:MagnifierBehavior ZoomFactor="4.0" LensRadius="160.0" />
I'm really becoming sold on behaviors; as I mentioned in an earlier blog post, they add a whole new dimension to Silverlight. I'll be demoing this new behavior (and probably others, too) at TechEd Europe.