Optimised Window Drawing

The last Woopsi development gave me optimised window erasing, so that when a window/other gadget moves, the area behind it can be redrawn quickly. The latest development gives me the counterpart to this - optimised redrawing of a window once it has moved to its new location (or is being drawn for the first time).

After a huge amount of refactoring and mucking about, I extracted the rectangle splitting code from the erase routine into a separate function. I can now pass it two vectors, one representing non-overlapped rectangles (which I pre-populate with the current gadget’s rectangle) and one representing overlapped rectangles (which, when the function has finished running, contains all of the rectangles overlapped by the gadgets we sent the vectors through). To obtain the list of rectangles for redrawing when a window is moving, we get the screen to fill the second list with all of the rectangles, from the uppermost window downwards, that overlap the moving window. This gives us all of the rectangles that need to be redrawn (each gadget draws its own rectangles as soon as those rectangles are calculated). This is the erase routine.

The draw routine uses the same splitting function, but does so in a different way. It still passes in the two vectors, and the first vector is still pre-populated with the dimensions of the gadget we want to re-draw. However, instead of working from the uppermost window and working downwards, we now work from the current gadget upwards. The routine works like this:

  • Create two vectors
  • Insert gadget’s dimensions into first vector
  • Send vectors to gadget’s parent gadget (ie. the window the gadget belongs to)
  • Locate the gadget in the gadget list
  • Starting from the next gadget higher up the list, split the rectangles
  • Repeat with the current gadget’s parent until we’ve recursed up the tree

At the end of all this recursion we get two vectors - the first contains all of the rectangular areas of the original gadget that aren’t overlapped by anything else higher in the display. The second contains all of the areas that are overlapped. We don’t care about the second vector for this operation, so we throw it away. We then feed each entry in the first vector into the clipped drawing command, which redraws just the visible portions of the gadget.

The major difference should be apparent - when erasing, we work from the top down. When drawing, we work from the bottom up.

The benefits of the new routine are that we no longer waste time drawing parts of the screen that will ultimately end up behind other parts as each successive layer gets drawn. It also gives us proper depth sorting for the SuperBitmap gadget, which now sits happily behind other windows.

I thought it’d be interesting to give solid window moving a go now that all of the drawing routines are in place. Adding this feature is simply a matter of commenting out the two XORed rectangle commands and adding in a “parent->eraseGadget(this)” and a “draw()” command. Simple windows zip around fast enough, but as soon as they overlap they flicker like crazy. Looks like the system isn’t fast enough yet to support this, but as I didn’t intend to have that feature anyway I’m not bothered.

The full set of changes this time around consists of:

  • Optimised drawing code
  • Tidied up erasing code
  • Added checks for empty non-overlap rectangle vector to the erasing code
  • Improved SuperBitmap initialisation speed

The TextWriter (the class that draws text to the screen) still isn’t clipping properly - that’ll be the next thing to look at. You can test the problem by stacking the three windows on the demo screen on top of each other. Drag the top window off and drop it somewhere else, and the title of the bottom window will get written over the top window. You can also half-obscure the screen depth gadget and click it (and hold it) - the glyph will be drawn over the title bar of the window you’ve obscured it with.

Here’s the latest demo:

Woopsi Demo V0.18


Jeff on 2007-09-27 at 03:50 said:

Looks quite pretty.

One problem I noticed, if you scroll the text so that half-a-line of text is showing at the bottom, then click in the Controls title bar, the bottom pixel of the text is drawn over the top of the 3d shadow.

ant on 2007-09-27 at 08:55 said:

Nicely spotted! I think this is related to the current clipping problems with the TextWriter (draws text to the screen) and the TextViewer (shows vertically-scrolling text) gadgets. There may be a left-over bug from the height/width/x2/y2 conversion problem, too.

ant on 2007-09-27 at 19:56 said:

Yep, it’s the TextWriter clipping problem - it clips the border and the rectangle that it clears before drawing itself, but I haven’t implemented the clipping routine for the text itself yet. My comment says “TODO: Implement a tidier version of the screen drawing routine for clipped rects”, so I must have noticed this one.