I saw a design of a mocked up start screen on Deviant Art that I liked more than the one in Windows 8. It had gradients, shadows, 3D depth definitely went against some of the design goals of metro.
Below is what the current Windows 8 “start screen” looks like :
Below is the mocked up deviant art “start screen” design :
What I liked about this is the use of gradients, shadows, depth to each tile ,… Pretty much all of those features are forbidden in current win8 metro design.
So I wanted to reproduce something similar using my favourite technologies XAML/C#/DirectX(SharpDx) ..
Analysing the deviant art “tile” we see the following..
1. Depth to each tile, almost a 3D like effect, achieved using some form of “Drop Shadow” effect
2. Inner radial gradient for each tile that gives an inner glow like effect surrounding the icons in each tile
It’s a very artsy/pastel like overall effect .. I like it a lot
So the question is how do we achieve this in WinRT XAML/C# ?
My proposed XAML/C# solution
When i see this design i immediately think Direct2D and Effect Graphs. The only way to get these shadow/Glow effects is using DirectX.
So my proposed solution would be to render it using SharpDx on a Direct2D surface utilizing effects. Further rendering this D2D surface in a XAML SurfaceImageSource (SIS) or SwapChainBackgroundPanel (SCBP).
Taking each of these parts lets create a tree of the UI proposed elements
Each tile will consist of the following visual elements that are rendered from bottom to top. This is the proposed “visual tree” of each tile.
Let’s Build it
As mentioned above I need to generate shadow/glow effects as well as a radial gradient so the only option for me is to use SharpDx.
There are numerous posts and examples of how to setup a SharpDx pipeline to render Dx content in XAML so I won’t be covering that here …
Step 1: Immediate Mode rendering requires me to maintain my own “visual tree”
In the XAML world, retained mode world, we are spoilt because a visual tree is maintained for us by the framework.
In the DirectX world, immediate mode world, we have to manage the rendering of each of the UI elements ourselves, including all the state between each frame!
I wrap all this logic in the Renderer classes…
I have a list that will hold the items for rendering “_renderTree”
Step 2: Preparing my assets
The assets for my tiles consists of
a) Background Images for the tiles
b) Icons for the tiles
All these assets will need to be loaded as textures and cached so that we don’t need to keep doing File IO reads.
To load a media element i have a method that wraps all that up in the BaseRenderer class. Its called “LoadAssetAsync”, and as the name suggests it loads it asynchronously. It uses the storage API’s to read the asset and then WIC to work with the bytes.
Once the asset is loaded from the file system i cache it for reuse later in case we need to re-render the entire _renderTree.
Loading assets and caching them for use as textures on a Direct surface is a very very difficult skill to learn, the only way to fully grasp this idea is to just throw yourself into the deep end and make many MANY mistakes
Step 3: Drawing the Tiles
We have our assets, we have a way to create a render tree next step is to load that tree with our UI elements that use these cached assets!
I have a method that wraps up the creation of the Tiles
The actual logic to create a tile is wrapped up in another method called “_createTile”
This is where things get interesting as now i need to work with Effect Graphs
Step 4: Drawing a Tile
Fist step is to create a “BitmapSource” effect, this will be the bottom most image, the background image.
This BitmapSource effect will be used as input into an effect graph.
Note that i use a variable passed in to define the “Background Image” to use, if you step thru the code you’ll notice it goes through “step 2” above to cache the asset so if we reuse this effect, or re render this UI element it will use the cached version.
As you can see below if we render just the BitmapSource effect it is the FULL image, it does not look like a tile at all. And it doesn’t honour the width/height of the proposed tile dimensions (in this example a tile is 200px by 200px)
If you noticed from Step 2 above each of the backgrounds are oddly shaped and not the classic “tile” shape of a square, or rectangle (2 squares). We need to somehow scale the background to fit into the tile shape we want..
I used a direct2d “Scale” effect, and this scale effect acts on the “bitmap source” effect from above. Notice the * above. This is an effect graph
Rendering this Scale effect, below is the result.
Now that the background image is scaled to fit the desired tile size we need to crop out the bits that fall outside. For this I use the “Crop” effect.
The input to the “Crop” effect is the previous “Scale” effect ( * above ). I should point out that the “Order” property above is used to order the layers for rendering, its like the z-index in web programming.
Rendering this “Crop” effect we get the 200px by 200px tile
So we used an effect graph to draw the background of a tile.
Step 5: Rendering the inner glow
The inner glow is a rectangle with a radial gradient applied to it. Its pretty simple, and i’ve used this in previous demos
It uses the RectangleGeometry in the SharpDx library
And the radial gradient applied
And rendered it looks like this:
Step 6: Rendering the outer shadow
The outer shadow is achieved using the “Shadow” effect. And the input to the shadow effect i’ll use the “Crop” effect from step 4 above. I could of used the scale or bitmapsource effect, as all i wanted was the outline of the tile.
The shadow effect rendered looks like this :
Step 7: icon & label
The Icon is rendered using a BitmapSource effect and to make it stand out I put a shadow effect around it . The reason i put a shadow around it is that if the background image blends too much into the icon it will be hard to see the icon.
This is the icon + shadow rendered:
The label is rendered using DirectWrite, ive wrapped the call into a method that does all the magic
Step 8: putting all the layers together
So rendering all the layers from step 4 to step 7 we get a tile
Zoomed in :
Step 9: other useful tips
I used a “staging” texture to render the tiles so that when the user pans and zooms it uses this “staged” texture to render rather than re-rendering the entire renderTree.
When rendering the tiles i swap in my “staging” bitmap. Then the tiles are drawn in this staged target
After the tiles are finished drawing on the staged bitmap i swap back in the original render target
Now I have all my rendered tiles in a nice staged bitmap that i can manipulate when panning/zooming.
So the actual rendering to D2D uses this “Staged” bitmap ..
As you can see from above i also render “Debugging” info and a nice “Designer Surface Region” that represents the screens dimension.
Final Result (part 1)
Running the WinRT-XAML-C# demo after the initial load the frame rate settles to around 55-60 frames per second (fps) which is what I expected.
A zoomed in view of a tile we see the outer shadow, inner radial gradient etc. Exactly the effects i was going for
Here’s a quick video of the demo running , its got a low fps due to screen recording and running simultaneously.
All my code can be found in my demo GIT project that i plan on using to also build my applications with.
Git Hub url : http://github.com/LiquidBoy/ModernApps
Part 1’s goal was to create tiles that have gradients, shadows and depth using an D2D effect graph. I pretty much achieved that
The next step, part 2, will be to add the animations to the tiles and wire up some interaction between XAML and the D2D surface.
D2D Effect Graphs mixed with XAML is a very cool combination that i hope to master over time, i will admit thou it is a hard concept and skill to grasp. Patience and lots of reading is required, plus lots of reverse engineering samples!
I haven’t had this much fun in ages , SharpDx gives us FULL access to DirectX / DirectWrite / WIC pretty much everything we need to create awesomeness from within XAML/C# ..
The fun continues with “part 2” where i try to animate all these tiles!