Click here to Skip to main content
Click here to Skip to main content

Custom AntiAliasing with GDI+

By , 30 Dec 2004
 

Introduction

I've been using GDI+ now for several years. I primarily use GDI+ when assisting in the development of custom UI's. I've noticed that complex vector graphics are being increasingly incorporated into UIs. The reason for this seems to be that graphic artist love to use antialaising, gradients, blends and transparency to give a "quality feel" to UI. I have strong bias toward the use of vector graphics because they are easy to manipulate and scale at runtime. Because of that we've been using GDI+ for awhile now to create custom UIs. However, I've recently run into several quality issues related to native GDI+ antialiasing that I thought would be of general interest to codeproject readers.

The issues I’ve recently seen are:

  1. Ugly Thumbnails – small vector drawing often look terrible when using the native GDI+ antialiasing. Often they are not easily recognizable from their original, larger images. Especially when the image is very small.
  2. Seams Show – Vector drawings which are constructed by abutting several objects in an attempt to make a seamless transition often have very noticeable artifacts.
  3. Unexpected Artifacts – some vector drawings have unexpected artifacts when antialiased using GDI+. Often these are caused by seams, but thin lines and other graphic elements can cause unpleasant artifacts.

Since my goal when using GDI+ is to produce a very high quality looking image, I needed a way to address these quality issues. Don't get me wrong, I'm not saying that the native GDI+ antialiasing is bad, frankly it's amazingly good and quite fast. However, there are times when it just isn't good enough.

The following sections in this article describe these artifacts in more detail and offer a simple algorithm that can eliminate most of these issues in your images -- at a steep price.

Cleaning Ugly Thumbnails

Thumbnails are small images that are often used to help a user see many possible selections at one time. It’s important that thumbnails be easily identifiable when very small; otherwise they are of limited value. Unfortunately the native antialiasing in GDI+ often produces very poor quality thumbnails (See the left and middle images above).

In the image above you can see that Native GDI+ antialiasing about 30% faster than custom antialiasing. Larger images can have up to 10x difference. However the quality of the custom antialiased thumbnail is very high and is much easier to associate with its original, larger image. Because of the increased rendering time, this custom antialiasing technique should only be used when absolutely necessary.

Are Your Seams Showing

Another case where GDI+ native antialiasing gets into trouble is when there are seams that are intended to be invisible in your art. This can be caused by several things:

Blends – Blends are essentially a gradient that morphs between two or more shapes. Since GDI+ doesn’t support blends directly they are usually constructed by creating multiple paths, one for each shape change, and filling those paths with the next calculated color in the gradient. Unfortunately this process can produce seams which alias badly when native antialiasing is turned on.

Abutting Objects - It's very common when building web graphics or when skinning an application to build bitmaps that are intended to appear seam-less when the graphics are abutted next to each other. There are times when using vector objects that this same technique would be useful. Unfortunately your seams will most likely show if you turn on antialiasing.

The artifacts that appear tend to be splits in the graphics or odd looking gradients (see examples in the image below).

Unexpected Aliasing

The native GDI+ antialiasing has trouble with small lines. In the example below, notice how the lines for the whiskers and around the text are oddly emphasized and aliased. This is not what the original artwork intended. This kind of aliasing gets worse as the image is scaled smaller.

The Custom Antialiasing Algorithm

To produce a higher quality image than native GDI+ antialiasing, I use the following algorithm.

  1. Construct an off-screen bitmap that is 2, 4 or 8 times larger that the image I intend to output. It is important to use a power of 2 for the offscreen bitmap.
  2. Draw the vector images scaled appropriately for the larger offscreen bitmap.
  3. Use Graphics.DrawImage with InterpolationMode set to HighQualityBicubic when stretch bliting the image to the smaller value.

The output using this algorithm is significantly better in most cases than the native GDI+ antialiasing method, but with a significant penalty in drawing time.

// Make a 4X offscreen bitmap, power of 2's are important because 
// interpolating other size images takes significantly longer.
Bitmap offscreen = new Bitmap(bounds.Width*4, bounds.Height*4);
Graphics ofg = Graphics.FromImage(offscreen);
                        
//Update transform for additional pixels
Matrix t = m.Clone();
t.Translate(-bounds.Left, -bounds.Top, MatrixOrder.Append);
t.Scale(4.0f, 4.0f, MatrixOrder.Append);
                        
//Do Paint
Paint(ofg, t);                    
                        
//Stretch blit the rendered image to the actual image
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(offscreen, bounds, 0, 0, offscreen.Width, offscreen.Height, 
    GraphicsUnit.Pixel); 
        

History

  • Originally written on Dec 29th, 2004.

Acknowledgments

The example program associated with this article, uses GDI+ vector graphics code that was created from example files from Adobe Illustrator CS (cheshire cat.ai and crystal.ai) and from Scott Ketterer (aquabutton.ai). I think these examples illustrate very well the complex graphics that are possible using GDI+ vector graphics and issues that can occur when using them.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Keith Rule
Web Developer
United States United States
Member
I work at Tektronix in Beaverton OR. I've been programming for fun since 1975 (I started while in a Computer Explorer Scout group in Spokane WA). I've been programming in C since 1979 and I've been working professionally since 1983.
 
I really enjoy www.codeproject.com. It has saved me an incredible amount of time. I only hope my small contributions have given back some of what I've taken.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
AnswerRe: Attached zip file is curreptmemberKeith Rule18 Oct '05 - 11:49 
It seems to be working now. I suspect that the codeproject website has been having some reliability issues. Hopefully that's been resolved.
 
Keith Rule
Generalreading .ai filesmemberanisk13 Mar '05 - 5:33 
Great code, Can you please point to sources of information that document how to read a .ai? How did you convert the .ai files to code? I need to render some simple illustrator format graphics.
 
thanks in advance
GeneralRe: reading .ai filesmemberKeith Rule14 Mar '05 - 6:13 
I can't give you the source and I'm not aware of any open source efforts that can does this. However there are three approaches you might consider when building an .ai conversion tool.
 
1) Illustrator can output pdf files. Use an open source pdf reader (like XPDF) to read the pdf file and then create a GDI+ emitter. I've tried this and it works. Turning PDF into GDI+ code isn't too bad because GDI+ has a very close mapping to postscript. The issue I had with this approach was that I didn't have access to all of the information I needed to emit code good code. I realized pretty quickly that I was either going have to write my own pdf reader or I was going to have to do something different.
 
2) Instead I built an Illustrator plugin using the illustrator SDK that can be downloaded from www.adobe.com. The SDK gives you access to all of the graphic elements in the Illustrator file. This approach works well. However, be warned that the SDK documentation is missing a bunch of details that you will have to discover on your own. There is also very little information available on the web about writing Illustrator plug-ins. Luckly there are several very good example files included in the SDK that will get you started.
 
3) My backup plan (if the first two things didn't work) was to use SVG. Illustrator can emit SVG and I believe there is at least one open source implimentation of an SVG reader. However, I didn't have to use this method, because writing a plug-in worked well for me.
 
Sorry I couldn't offer more help. I hope this gets you started.
 

 

 
Keith Rule
GeneralNot entirely correct: pen widths not adjusted.memberwout de zeeuw1 Jan '05 - 7:57 
The features you describe with the small lines can be attributed to the fact that you don't correct the pen widths that should also get bigger when you blow up your intermediate bitmap. So you draw with relatively thin pens after scaling back to the normal world.
 
Hence this is not just custom antialiasing, but you make the effective pen widths smaller.
 
Kind regards,
 
Wout

GeneralRe: Not entirely correct: pen widths not adjusted.memberKeith Rule2 Jan '05 - 14:27 
That's a good point. I will look into it further. If necessary, I will update the article to reflect this.
 
Thanks!
 
Keith
GeneralRe: Not entirely correct: pen widths not adjusted.memberKeith Rule3 Jan '05 - 11:55 

wout de zeeuw wrote:
The features you describe with the small lines can be attributed to the fact that you don't correct the pen widths that should also get bigger when you blow up your intermediate bitmap. So you draw with relatively thin pens after scaling back to the normal world.
 
Hence this is not just custom antialiasing, but you make the effective pen widths smaller.
 
Kind regards,
 
Wout

 
I've thought about this a bit more and I think I see your point. You are right, the fact that I'm enlarging the image does effect the custom antialiasing by providing additional information. But I believe the underlying pens are scaling correctly (meaning the pen widths scale with the rest of the image) and that the artifacts I'm showing are real GDI+ issues. If didn't understand your point, or I'm mistaken about the pen width scaling properly please let me know.
 
Thanks again for your comments.
 
Keith
 

GeneralRe: Not entirely correct: pen widths not adjusted.memberwout de zeeuw3 Jan '05 - 22:46 
Hi Keith,
 
If I draw a line with pen width 2 on a bitmap that's 4 x 4, and then scale the bitmap to be 2 x 2, then the effective pen width as rendered on screen will be twice smaller, hence 1.
 
Since you probably use pen width 1 on the big bitmap, it will get proportionally smaller depending how much you downscale the bitmap. Hence by choosing a initial bitmap size, you are effectively choosing a pen width.
 
Kind regards,
 
Wout

GeneralRe: Not entirely correct: pen widths not adjusted.memberwout de zeeuw4 Jan '05 - 0:32 
Hi Keith,
 
I must correct my statements! You indeed set the matrix transform, so the pen widths are scaled when drawing on the bigger bitmap.
 
I experimented a bit with setting other pen widths, since it's possible to set a float value to a pen width and I was wondering if it got similar results with pen widths smaller than 1f. GDI+ however doesn't react to widths smaller than 1f, so that's the minimum. And the whiskers of the cat are not draw with a pen, but drawn with a graphics path and then filled, so here pen width doesn't apply; it's a very thin path that is filled.
 
So indeed your approach does work. But application of it especially pays off when needing to draw features that are of subpixel size.
 
Kind regards,
 
Wout

GeneralRe: Not entirely correct: pen widths not adjusted.memberKeith Rule4 Jan '05 - 5:36 
wout de zeeuw wrote:
Hi Keith,
 
I must correct my statements! You indeed set the matrix transform, so the pen widths are scaled when drawing on the bigger bitmap.
 
I experimented a bit with setting other pen widths, since it's possible to set a float value to a pen width and I was wondering if it got similar results with pen widths smaller than 1f. GDI+ however doesn't react to widths smaller than 1f, so that's the minimum. And the whiskers of the cat are not draw with a pen, but drawn with a graphics path and then filled, so here pen width doesn't apply; it's a very thin path that is filled.
 
So indeed your approach does work. But application of it especially pays off when needing to draw features that are of subpixel size.
 
Kind regards,
 
Wout

 
When I said that lines don’t always antialias correctly, I was referring to strokes around paths, very small filled paths (like the one you just mentioned) and lines. In other words, anything that looks like a line. I probably should have been clearer about that in the article.
 
However, this example does show that an artist may choose a method for building a vector object that isn't necessarily the best approach when the image is scaled using GDI+ antialaising. I've found that communicating gnarly details like this to an artist can be challenging. With issues like this, I think our artists feel that we (programmers) make arbitrary rules that just get in their way. Maybe they're right about that.

 
Keith
Generalvery slowmemberMario M.30 Dec '04 - 22:04 
It's 6 times slower.
 
On AMD XP2800+, with ATI AIW 9800
Aqua
GDI+ = 0.027, Custom = 0.158 (6 times slower)
 
Glass
GDI+ = 0.024 Custom = 0.278 (11 times slower)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 31 Dec 2004
Article Copyright 2004 by Keith Rule
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid