Archive for July, 2006

July 31st 2006

Useless fact

Banging your head against a wall uses 150 calories an hour.

3 Comments »

July 28th 2006

How to Draw Flicker Free

A common problem developers have is drawing to the screen flicker free. What is flicker? Flicker occurs when drawing happens in many layers and it is visibly refreshed. You will see, albeit for a very short time, a flash of white (or the window background) then your content. This causes a “flicker” appearance. It is very hard to get flicker on Mac OS X because the window manager double buffers your window. Also, Linux does a little bit of buffering as well. However, Windows and Mac OS 9 don’t do any. How can you draw flicker free always with optimal performance and code reusability?

Let’s pretend that you’re writing a custom control by subclassing Canvas. We’ll start with my immedate pointers:

  • Implement the paint event.
  • Uncheck the EraseBackground property.
  • Double buffer drawing as needed
  • Call Refresh(false), not Refresh().

Implement the paint event

Implementing the paint event is the most important thing a custom control needs to do. The Paint event is called whenever the control needs to be redrawn, such as when it moves, activates/deactivates, resizes, etc. If you don’t have any code in this event, your control will “randomly” erase.

Uncheck the EraseBackground property.

This property was introduced sometime in the REALbasic 2005 cycle, if I recall correctly. This property tells REALbasic that your control will redraw every pixel within its bounds during the paint event. Why is this helpful? If EraseBackground is true, REALbasic must first clear the area of the canvas and then call your Paint event. This action will cause visible flicker. If EraseBackground is false, REALbasic simply calls your Paint event. What if your control doesn’t take up the entire area? This is common for “rounded” controls — ie, buttons like on Mac OS X. In this case you can always draw the background color of the window and then the content of your control. This won’t cause flicker when combined with double-buffering. Speaking of which…

Double buffer drawing as needed

Double-buffering is a technique in which you draw everything offscreen, and then draw the result to the screen. This has the effect of only doing one draw operation to the screen, instead of potentially hundreds. This has the visual effect of eliminating flicker entirely. How do you double-buffer in REALbasic? It’s relatively straightfoward — draw to a temporary Picture, and then draw that picture to the Graphics instance passed into the Paint event.

Call Refresh(false), not Refresh().

The Refresh method began taking this optional parameter at some point in REALbasic 2005 also. The Boolean specifies whether or not refreshing should first erase the background of the control being refreshed. This is very important for transparent controls, but for custom controls, it’s not important if you follow these tips in this article.

Some people say to always avoid Refresh like it’s the plague. However, I say: don’t avoid it, but rather always use it! Why do I say that? Because the OS does a lot of work for you to set up the Graphics instance in the Paint event to be exactly what you need to draw into. This involves clipping, “locking the view”, etc. While the non-HIView drawing system allowed drawing outside of the paint event, Mac OS X prefers to update everything in one pass which allows the OS to optimize how often it uploads information to the graphics card.

So, don’t avoid Refresh. Simply call Refresh( false ) for now, and if in the future REALbasic introduces an API such as Invalidate, use it. This will ensure that your paint event fires, and everything is as optimal as possible.

That all sounds great, but the question remains: How?

Good question. Now that we have stated our goals, let’s go over a few implementation ideas:

  • Create a public Redraw method
  • Have the Paint event call the Redraw method.
  • Manage your buffers smartly
    • Don’t double buffer on systems that don’t need it*
    • *If necessary for speed, buffer on all systems, but limit the redraw area.

Simple enough? Let’s go a bit more in-depth:

Create a public Redraw method

This one is simple enough. Remember when I said do all of your drawing in the Paint event? Well, I didn’t mean to put all of your code directly into the Paint event. Instead, take this approach: Create a method “Sub Redraw( g as Graphics )” and put all of your code there. Why? Because it allows you to draw your control in different contexts. Want to print? Pass in your printer graphics context. Want to save the rendered picture to disk? Draw it to a temporary picture by passing the picture’s graphics into the Redraw method.

Have the Paint event call the Redraw method.

This is as easy as it sounds. Your paint event will end up having only a few lines of code — the double-buffering code and a simple call to Redraw.

Manage your buffers smartly

This is crucial. Creating a new picture can be very expensive. Simply add a new property to your Canvas subclass “mBuffer as Picture.” In the paint event, use this code:

Sub Paint( g As Graphics )
If mBuffer Is Nil Or mBuffer.Width < g.Width Or mBuffer.Height < g.Height Then
mBuffer = New Picture( g.Width, g.Height, 32 )
End If

Redraw( mBuffer.Graphics, g.Width, g.Height )

g.DrawPicture mBuffer, 0, 0, g.Width, g.Height, 0, 0, mBuffer.Width, mBuffer.Height
End Sub

This assumes that your Redraw method additionally takes a “renderWidth” and “renderHeight” parameter instead of using the width and height of the graphics class. How much time can this save? Just imagine how many processor cycles are wasted by allocating a decent-size chunk of memory and filling it with “white”. Now, just imagine doing that at 60-FPS. Why would you care about getting your control to draw that fast? Simple: When resizing a window, your control will draw that fast. You need to be prepared, or face serious consequences of slow resizing, which just looks ugly.

Don’t double buffer on systems that don’t need it*

Simple enough: Mac OS X double buffers for you. So, if all you’re doing in your Redraw method is overdrawing everything, don’t go through the mBuffer picture on Mac OS X. It’ll speed things up a little bit more.

*If necessary for speed, buffer on all systems, but limit the redraw area.

Continuing the last point — If your control is very complicated with many sub-sections, it is possible to redraw only “dirty” parts of your buffer. If this is the case, this is definitely the way to go. While it’s often hard work to manage the dirty state of a region, it can yield incredible speed benefits. However, techniques on how to do this are beyond the scope of this article, and is only here because I’m sure some readers have considered this option before.

Putting this into practice

No doubt that while reading this you’ve thought about a project of yours and thought about how some of this might apply to your project. Some of it will be straight-forward. Other times, it may not be. The most extreme case is writing something as complex as, for example, a text editor (or in my history, a Code Editor) or a list control (haven’t we all attempted this now?). These tips aren’t required for you to write a program in REALbasic. But if you want to minimize effort on your part, and greatly increase the likelyhood that your control will be a good citizen (ie, not slowing down window resizing, not flickering, etc), these tips can work as guidelines to help you in succeeding.

6 Comments »

July 25th 2006

I’m a Married Man

So where have I been for the last few weeks? Well, long story short: I got married.

Short story a bit longer: On July 7, the day after my birthday, I began my road trip from Austin, TX to Washington, NJ. I finally got packed and ready, leaving my house at about 10:30, and after completing a couple errands, actually left Austin at 11:30. On my way!

I drove about 4 hours, stopping at around Greenville, TX. Slept in a Motel 6 until I left at about 9:30, on the road again. My next day journey was from there to Lexington, KY, a full 13.5 hours of driving. I stayed in a Quality Inn that wasn’t very high quality, but it sufficed for the night. Finally, I finished up my drive to Washington, NJ and got in at about 8:00 after another 10 hours of driving. Total trip mileage: 1,735 miles (2792 km).

I then worked remotely for the week leading up to the wedding. Some weird hours some days, working around picking relatives up at Newark Airport (another 6-8 hours of driving that week). Finally, the day came: July 15. It was a great day, and now I have my better half.

I then went completely out of touch with the world except for my wife, Erin. My cell phone was off, computer left in its bag, no internet access, nothing. For an entire week. It felt wonderful. Of course, there’s a downside to being incommunicado — my work inbox was overflowing with 1171 messages. It literally took me hours to go through it this morning, but I’m caught up now.

I’m working on a good post called How to Draw Flicker Free. While it’ll teach a newbie to draw flicker free, it’ll also teach even advanced users why to draw the way I’m teaching it. It’s coming along nicely, but I figured it was easier to post this than it was to try to finish that up this morning :P

8 Comments »