Thursday, November 5, 2009

Drawing Shapes - Silverlight WriteableBitmap Extensions III

Last week I've released the second part of my WriteableBitmap extensions methods. I've added DrawLine() methods and presented a sample application that showed that the WriteableBitmap line-drawing methods are 20-30 times faster than the UIElement Line class.

A stable and fast line-drawing algorithm is the basis for most shapes like triangles, rectangles or polylines in general. For this blog post I've extended the WriteableBitmap with some specialized methods for various shapes including a fast ellipse rasterization algorithm.


Live

The application includes various scenarios where different shapes are drawn. By default a little demo is shown that I call "Breathing Flower". Basically different sized circles rotating around a center ring are generated. The animation is done using the best trigonometric functions in the world: sine and cosine.
The scenario "Static: WriteableBitmap Draw* Shapes" presents all shape extensions currently available. From left to right: Points - SetPixel(), Line - DrawLine(), Triangle - DrawTriangle(), Quad - DrawQuad(), Rectangle - DrawRectangle(), Polyline - DrawPolyline(), closed Polyline - DrawPolyline(), Ellipse - DrawEllipse(), Circle - DrawEllipseCentered().
The other two scenes randomly draw all shapes or only ellipses and allow controlling the work load by setting the number of shapes. The Silverlight frame rate counter at the upper left side shows the current FPS in the left-most column.

How it works
Most of the new extension methods use the DrawLine() function to build up a shape. Only the DrawRectangle() method implements a simplified line drawing using some for loops which is faster than calling the DrawLine() method four times. The DrawEllipse() function implements a generalized form of the Midpoint circle algorithm. I've used "A Fast Bresenham Type Algorithm For Drawing Ellipses" from this paper by John Kennedy.
The extension methods are pretty fast and if you need to draw a lot of shapes and you don't need anti-aliasing, Brushes or other advanced UIELement properties, the WriteableBitmap and the Draw*() extensions methods are the right choice. If you don't like the sharp edges, you can apply the Silverlight 3 Blur effect to the image:




The signature of the extension methods
DrawPolyline(this WriteableBitmap bmp, int[] points, Color color);
DrawPolyline(this WriteableBitmap bmp, int[] points, int color);

DrawTriangle(this WriteableBitmap bmp, 
             int x1, int y1, int x2, int y2, int x3, int y3, Color color);
DrawTriangle(this WriteableBitmap bmp, 
             int x1, int y1, int x2, int y2, int x3, int y3, int color);

DrawQuad(this WriteableBitmap bmp, 
     int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, Color color);
DrawQuad(this WriteableBitmap bmp, 
     int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int color);

DrawRectangle(this WriteableBitmap bmp, 
              int x1, int y1, int x2, int y2, Color color);
DrawRectangle(this WriteableBitmap bmp, 
              int x1, int y1, int x2, int y2, int color);

DrawEllipse(this WriteableBitmap bmp, 
            int x1, int y1, int x2, int y2, Color color);
DrawEllipse(this WriteableBitmap bmp, 
            int x1, int y1, int x2, int y2, int color);

DrawEllipseCentered(this WriteableBitmap bmp, 
                    int xc, int yc, int xr, int yr, Color color);
DrawEllipseCentered(this WriteableBitmap bmp, 
                    int xc, int yc, int xr, int yr, int color);
The DrawPolyline() method uses an array of x- and y-coordinate pairs and the array is interpreted as (x1, y1, x2, y2, ..., xn, yn). If a closed polyline should be drawn, the first point must also be added at the end of the array.
The DrawTriangle() and DrawQuad() methods needs all shape points as x- and y-coordinates. The DrawRectangle() function plots a rectangle out of the points that represent the minimum and maximum of the shape. The DrawEllipse() method interprets the parameters the same way, but the DrawEllipseCentered() function takes the center of the ellipse and the radii as arguments.
All methods are available for the Color structure or an integer value as color.

Usage
// Initialize the WriteableBitmap with size 512x512
WriteableBitmap writeableBmp = new WriteableBitmap(512, 512);

// Set it as source of an Image control
ImageControl.Source = writeableBmp;


// Fill the WriteableBitmap with white color
writeableBmp.Clear(Colors.White);


// Black triangle with the points P1(10, 5), P2(20, 40) and P3(30, 10)
writeableBmp.DrawTriangle(10, 5, 20, 40, 30, 10, Colors.Black);

// Red rectangle from the point P1(2, 4) that is 10px wide and 6px high
writeableBmp.DrawRectangle(2, 4, 12, 10, Colors.Red);

// Blue ellipse with the center point P1(2, 2) that is 8px wide and 5px high
writeableBmp.DrawEllipseCentered(2, 2, 8, 5, Colors.Blue);

// Closed green polyline with P1(10, 5), P2(20, 40), P3(30, 30) and P4(7, 8)
int[] p = new int[] { 10, 5, 20, 40, 30, 30, 7, 8, 10, 5 };
writeableBmp.DrawPolyline(p, Colors.Green);


// Render it!
writeableBmp.Invalidate();

Source code
You can download the Silverlight application's source code including the complete and documented ready-to-use WriteableBitmapExtensions file from here. Check out my Codeplex project WriteableBitmapEx for an up to date version of the extension methods.

To be continued...
For the next part of this series I'm planning to add fill extensions methods to the WriteableBitmap like FillRectangle(), FillEllipse(), etc.

Update 11-06-2009
Nokola optimized the DrawLine() function a bit and made it 15-30% faster than the standard DDA implementation. I've replaced the DrawLine() method in the extensions with Nokola's optimized version, fixed some bugs and updated the source code. The original DDA implementation is now called DrawLineDDA().
Thanks Nikola!

Update 11-11-2009
Nokola optimized the DrawRectangle() function and I've updated the implementation of it. I've also added a faster Clear() method without parameters that fills every pixel with a transparent color. This was also proposed by Nokola.
Thanks again Nikola!

Wednesday, November 4, 2009

Classic Rock evolved into Heavy Metal

Mihnea Balta released MetalScroll, a great replacement for the superb Visual Studio Add-In RockScroll:









MetalScroll is an alternative for RockScroll, a Visual Studio add-in which replaces the editor scrollbar with a graphic representation of the code. Compared to the original, this version has a number of improvements:

  • The widget at the top of the scrollbar which splits the editor into two panes is still usable when the add-in is active.
  • You must hold down ALT when double-clicking a word to highlight all its occurrences in the file
  • Lines containing highlighted words are marked with 5x5 color blocks on the right edge of the scrollbar, to make them easier to find (similar to breakpoints and bookmarks).
I have used RockScroll for a while, but encountered some bad Visual Studio crashs if I changed from code view to the Windows Forms Designer and as a consequence I had to uninstall it. Later I asked Scott Hanselman (@shanselman), who released RockScroll on his website, if he could contact the RockScroll author Rocky Downs and ask him for the source code, so I could fix it and might release it as open source, but Scott Hanselman never got an answer from the RockScroll author. You can imagine how pleasantly surprised I was after I had read the MetalScroll news from Johan Andersson (@repi) today.



My favorite feature is not the smart Scrollbar nor the cool splitter, it's the word occurrence highlighting, a feature that I missed since I worked with Eclipse years ago.
MetalScroll works with Visual Studio 2005 and 2008 and it's free and even open source! So don't hesitate, download and install this great Visual Studio add-in now. You won't be disappointed.

Thanks Mihnea Balta for your awesome work!

Thursday, October 29, 2009

Drawing Lines - Silverlight WriteableBitmap Extensions II

The WriteableBitmap class is a nice feature that was added in Silverlight 3. It could be used to generate fast procedural images by drawing directly to a bitmap. The WriteableBitmap API is very minimalistic and there's only the raw Pixels array for such operations. This Property stores a 32 bit integer as color value for each pixel of the WriteableBitmap.

A couple of months ago I've written a handful of SetPixel methods that made it easier to use the WriteableBitmap, but they only provided a better interface for the Pixels Property and had no real functionality. This time I've written a more algorithmic method which performs a rasterization of a line: The famous DrawLine(). As the name implies is it used to draw a line between two points in the bitmap. To say it in advance: This is the way for drawing huge amounts of lines in Silverlight. I've used the elegant C# 3.0 extension methods again to add this functionality to the existing WriteableBitmap class.

Live

The application includes various scenarios where line-drawing is performed and it is also possible to set the number of lines to control the work load. The Silverlight frame rate counter at the upper left side shows the current FPS in the left-most column. You can find some details about the other parameters in this excellent blog post by András Velvárt(@vbandi).
For comparison I've also added a randomized line drawing scenario using the UIElement Line.

How it works
I've implemented two common line-drawing algorithms: The well-known Bresenham algorithm that uses only cheap integer arithmetic and a Digital Differential Analyzer (DDA) which is based upon floating point arithmetic. I don't want to explain the algorithmic details that were already explained several times in the literature or on the web. The linked Wikipedia articles are a good starting point if you are interested.

The signature of the extension methods
DrawLine(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color);
DrawLine(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color);

DrawLineBresenham(this WriteableBitmap bmp, 
                  int x1, int y1, int x2, int y2, int color);

Clear(this WriteableBitmap bmp, Color color);  
The default DrawLine() method uses a DDA algorithm and is available for the Color structure and an integer value as line color. Furthermore it needs the x and y coordinate of the start point (x1, y1) and the end point (x2, y2) of the line.
I have also added a Clear() method that fills the whole WriteableBitmap with a Color.

Usage
// Initialize the WriteableBitmap with size 512x512
WriteableBitmap writeableBmp = new WriteableBitmap(512, 512);

// Set it as source of an Image control
ImageControl.Source = writeableBmp;


// Fill the WriteableBitmap with white color
writeableBmp.Clear(Colors.White);

// Draw a black line from P1(10, 5) to P2(20, 40)
writeableBmp.DrawLine(10, 5, 20, 40, Colors.Black);

// Render it!
writeableBmp.Invalidate();

Results
The WriteableBitmap line-drawing approach is more than 20-30 times faster as UIElement Line. So if you need to draw many lines and don't need anti-aliasing or other UIELement properties, the DrawLine() extensions methods are the right choice.
One interesting fact: The Bresenham algorithm that uses only simple integer operations is not the fastest line-drawing algorithm anymore. It was one of the earliest computer graphics algorithms invented in the 1960s. Since then the hardware has changed dramatically and a simple floating point DDA technique gives almost the same results on modern hardware.
Please keep in mind that these results may differ depending on the used hardware.

Source code
You can download the sample application as a Visual Studio 2008 solution including the complete and documented ready-to-use WriteableBitmapExtensions class from here. Check out my Codeplex project WriteableBitmapEx for an up to date version of the extension methods.
Have fun and let me know it if you do some cool stuff with it.

To be continued...
I'm planning to write more shape extensions for the WriteableBitmap like DrawRectangle(), DrawEllipse(), DrawPolyline(), etc. I will publish them in follow-up blog posts.

Update 11-06-2009
Goto Drawing Shapes - Silverlight WriteableBitmap Extensions III

Monday, October 26, 2009

Silverlight Live Streaming being discontinued. What now?


The Microsoft Live team announced in a blog post that the Silverlight Streaming service is being discontinued. The Silverlight Streaming service offered free hosting for Silverlight applications and videos.




The blog post states:
"Microsoft Silverlight Streaming by Windows Live Beta is being discontinued and will eventually be taken down.
A new Windows® Azure(TM)-based hosting and delivery service will be launched by the end of 2009, though this is not a direct replacement for Silverlight Streaming and will have costs associated with its use."
Further they write:
"Don't panic! All your current content is safe and you will receive sufficient notice for you to make an informed decision on where to host your Silverlight content and applications. However, in the interim, we would like you to be aware of the following:

Effective immediately, no new sign-ups are permitted for the Silverlight Streaming service. In addition, the Silverlight Streaming publishing plug-in for Expression Encoder will no longer be available for download.
The new Windows Azure functionality will not be a direct replacement for the Silverlight Streaming service and will be a paid subscription service."


The blog post also gives some instructions on how to retrieve the hosted Silverlight content. One can't really say that the offered (Azure) "migration process" is comfortable and frictionless. Actually, as far as I know there is no real Azure migration service at the moment, one can only access the Silverlight Streaming file system and copy the content.

What now?
In my opinion this is not a very nice move of Microsoft for bloggers like me. I have used Silverlight Streaming to provide live examples for my Silverlight posts. I mainly blog about Silverlight development and provide content and source code for free, therefore I help Microsoft to spread their technology - for free. It's a shame that a free streaming service at least for personal / educational purposes like mine isn't offered anymore. I might see things too naive and got the intention of Silverlight Streaming wrong, but from my point of view it's definitely not nice.
For now I have uploaded the samples and Videos to my public Dropbox folder and stream them from there. Dropbox uses Amazon's S3 storage system to store files.
Timothy Parez (@delegatevoid) pointed me on this alternative and it actually works fine. Thanks Timothy!
I hope this blog post from the Silverlight Streaming team does not reflect the final decisions for Microsoft's Silverlight hosting. At least I can hope...

Saturday, October 24, 2009

Read Between The Pixels - HLSL Kill Pixel Shader


A couple of days ago William Moore (@codenenterp) asked me via Twitter if I know a pixel shader that makes odd pixels black. I didn't know were such a shader is available, but I knew that it should be not hard to write one in HLSL. So I fired up the Shazzam tool and wrote two shaders.






I have started with a pixel shader that kills the color in every odd row of the texture and makes every second scanline transparent. If a black rectangle is overlaid with such a shaded image, each odd scanline will be seen as black, thus resulting in the desired black scanline effect.




An alternating line effect could be realized if an image instead of a black rectangle is overlaid.




After that I've extended the pixel shader to kill every odd pixel to produce the checkerboard effect William Moore wanted.




This is how a Silverlight Button could look like if the shader is applied to it:




How it works
The pixel shaders are pretty simple and small. As always, I've commented the HLSL code, but if you have any further questions, feel free to leave a comment.
Here is the shader that kills every odd scanline:
// Parameter
float2 TextureSize : register(C0);

// Sampler
sampler2D TexSampler : register(S0);

// Shader
float4 main(float2 texCoord : TEXCOORD) : COLOR 
{ 
   // Default color is fully transparent
   float4 color = 0;

   // Scale to int texture size
   float row = texCoord.y * TextureSize.y * 0.5f; 

   // Calc diff between rounded half and half to get 0 or 0.5
   float diff = round(row) - row;
   float diffSq = diff * diff;

   // Even or odd? Only even lines are sampled
   if(diffSq < 0.1)
   {
      color = tex2D(TexSampler, texCoord);
   } 
   return color;
}

And here the shader that kills every odd pixel:
// Parameter
float2 TextureSize : register(C0);

// Sampler
sampler2D TexSampler : register(S0);

// Shader
float4 main(float2 texCoord : TEXCOORD) : COLOR 
{ 
   // Default color is fully transparent
   float4 color = 0;

   // Scale to int texture size, add x and y
   float2 vpos = texCoord * TextureSize * 0.5f; 
   float vposSum = vpos.x + vpos.y;
 
   // Calc diff between rounded half and half to get 0 or 0.5
   float diff = round(vposSum) - vposSum;
   float diffSq = diff * diff;

   // Even or odd? Only even pixels are sampled
   if(diffSq < 0.1)
   {
      color = tex2D(TexSampler, texCoord);
   } 
   return color;
}

As you can see the only difference is that the kill pixel shader uses the sum of the texture coordinate's x and y component to determine if the pixel is even or odd.
For the effect of the uppermost blog post image (mandrill), I've used the product of x and y instead of the sum.
The size of the killed pixel group could be changed with the TextureSize parameter. The following example uses an eight of the original picture size as TextureSize.




Source code
I have also written a small Silverlight application that shows an image with the pixel shader attached. You can download the Visual Studio 2008 solution including the pixel shaders from here.

Photos from the USC-SIPI Image Database