Archive

Archive for the ‘Woopsi’ Category

So What Else is New?

September 8th, 2010 Ant 1 comment

I’ve made a few changes in addition to the damaged rectangle stuff. There were a few memory leaks dotted around the code, most of which were the result of early-exiting functions so that string iterators weren’t deleted. Xcode’s “Leaks” instrument is absolutely indispensable when trying to track memory leaks down.

I’ve removed AmigaWindow::redrawBorder() because it wasn’t used and didn’t appear to do anything. In its place I’ve added markBorderDirty(), which uses the new damaged rectangle system to redraw just the borders. This speeds things up a little when windows gain or lose focus - only the borders change, so why redraw the entire window?

The Text class is now called “Document”. It no longer inherits from the WoopsiString class, but contains one instead. The Text class always emanated a nasty code smell. What does “Text” mean, anyway? Is it a string? Sort of… It’s a string that has a font attached and a width within which it wraps itself. Kind of like a Word document. Ahhh, that makes sense: It’s a document.

Gadget::checkCollision() works correctly. I think I broke it a couple of releases ago.

ScrollingPanel::scroll() works correctly when the panel is on the top DS screen. Lakedaemon spotted this bug. The fix involved converting between co-ordinate systems.

The ListData class’ destructor no longer fires list changed events when it runs.

Finally, Woopsi will no longer ship with Lakedaemon’s freetype classes or the freetype library. Instead, they can be downloaded as part of Lakedaemon’s suite of DS libraries and Woopsi classes, “ndsToolKit”:

It includes Woopsi, the XmlBox gadget, and the freetype classes and library, in addition to DS ports of libjpeg, libpng, libsqlite3 and others. Sounds very powerful.

Categories: Woopsi Tags: , ,

Redrawing with Damaged Rects

September 8th, 2010 Ant 1 comment

The Problem: Bad APIs and Suboptimal Redrawing

The existing gadget redrawing system in Woopsi sucks. It’s inefficient and makes the API ugly.

For example, here is a completely bare-bones startup method that will get Woopsi into a usable (but empty) state:

void SomeApp::startup() {
	enableDrawing();
	redraw();
}

The two function calls in the startup() method ensure that the Woopsi instance is visible. When an instance of the Woopsi class is created, it’s redraw system is disabled. This allows the initial set of gadgets to be added without them appearing on-screen one at a time. Once the initial state has been set up, drawing can be enabled, and the initial state can be drawn.

You might notice that these two lines of code aren’t required in any other UI framework you’ve used. Neither winforms, AWT, Swing, GTK, KDE or anything else requires that you manually enable redrawing and redraw the display once you’ve finished adding UI components to your application.

The reason for this is the way that Woopsi handles redrawing. In Woopsi, each gadget is responsible for redrawing itself. All gadgets know which portions of themselves are visible, and they also know when their appearance has been changed. When a gadget is created it knows that it must redraw itself, and does so. Likewise, when a gadget moves, or is resized, or is clicked, it will redraw itself. So, when we add gadgets to Woopsi, they will draw themselves to the screen one at a time instead of the entire screen being drawn in a single operation. This piecemeal redraw looks odd and wastes CPU time, so drawing is initially disabled. Users must re-enable drawing themselves when the UI is ready.

The problem of wasted CPU cycles is more onerous in other situations. Consider the TextBox gadget. This redraws itself every time its text is changed. If we run the following code, the textbox will redraw four times:

textbox->appendText('t');
textbox->appendText('e');
textbox->appendText('x');
textbox->appendText('t');

The textbox redraws every time a character is added to its text. If we were to add one hundred characters, the textbox would redraw one hundred times. Now suppose that the textbox fills the entire bottom DS screen. Changing the text in the textbox changes roughly 8x8, or 64 pixels, per update. That means we’ve changed 6,400 pixels. However, we’ve redrawn every pixel on the screen one hundred times. We’ve redrawn 256x192, or 49,152 pixels, per update. We’ve redrawn 4,195,200 pixels when we only needed to redraw at most 6,400. 99.87% of the redrawing was just wasted CPU time.

Woopsi offers a workaround in the form of the enableDrawing() and disableDrawing() methods of the Gadget class:

textbox->disableDrawing();
textbox->appendText('t');
textbox->appendText('e');
textbox->appendText('x');
textbox->enableDrawing();
textbox->appendText('t');

However, this is a nasty hack. It requires that the developer knows enough about Woopsi’s internals to be able to predict when it will redraw and when it is safe to prevent the automated redraws from running. In the above code, it isn’t particularly obvious why drawing is re-enabled before the last character is added (so that the final append triggers a redraw) and it isn’t something that developers should need to care about.

The Competition: What Everyone Else Does

In the majority of other UI kits, redrawing is handled by tracking damaged rectangles. These are areas of the screen that have been modified and therefore need redrawing. In these systems, instead of our textbox redrawing itself, it would notify an object responsible for tracking these damaged rectangles that its visible areas had changed. The tracking object would store any of those rectangles that it hadn’t been previously notified of in a list. At frequent intervals (every VBL, say) the tracker would order the gadget hierarchy to redraw all of the damaged rectangles in its list. Using this system, the textbox would only be drawn once no matter how many times it was changed.

Justifying Woopsi’s Behaviour: DMA and Bad Code

Woopsi doesn’t use a damaged rectangle system for two reasons. Firstly, it makes extensive use of the DS’ DMA hardware to copy areas of the framebuffer around. It is especially important to the scrolling system that everything on-screen is up-to-date. If redrawing was postponed until a later time it is likely that the scrolling system would be working with out-of-date data. The second reason is the complexity of the rectangle splitting algorithm discussed here and here. Trying to get the code that splits up rectangles to do anything slightly different would be horribly complicated because, well, the code itself is horrible.

Potential Benefits of Changing Woopsi

But what if Woopsi could use the damaged rectangle system for redrawing? How would that affect the API and the framework? It would instantly obsolete about a dozen of the most complicated methods, and it would make a bare-bones Woopsi setup function look like this:

void SomeApp::startup() { }

Adding one hundred characters to a textbox would require a single redraw without any extra developer effort. Even actions such as moving gadgets would be more efficient - they are currently entirely erased from one location and redrawn at another, even if they move by just one pixel. A damaged rectangle system would reduce the number of rendered pixels in such a situation by almost 50% because there wouldn’t be an erasing step.

Fixing the Rectangle Splitter

The existing code used to split up rectangles into intersecting and non-intersecting sub-rectangles is 207 lines long. At times, it has nested if statements and for loops nearly a dozen deep.

(I’d originally intended to add the code in here, except my current WordPress theme breaks the “more” tag, making the post far too long for the front page of the blog. Additionally, trying to force the code into a smaller div by adding a “style” attribute breaks the syntax highlighter and causes angle brackets to be interpreted as HTML tags. I really need to get a new theme that’s less cluttered and easier to read, but nothing I’ve found so far fits.)

Anything nested that deeply is in dire need of refactoring. I looked at it the other day and realised that the entire algorithm was vastly overengineered. A far more efficient approach presented itself to me and I set about rewriting it. The replacement method is 74 lines long and contains just 4 if statements with no nesting at all.

The new method is a massive improvement over the original code. It is considerably smaller and easier to understand, it is faster and best of all, it produces the optimal result each time.

In all situations where there is an intersection, the original code produced (2n-1) rectangles, where n is the optimum number of rectangles. The larger the number of rectangles produced by this method, the more clipping the renderer will have to do later, so having a function that always produces the optimal result is a very good thing.

Switching to Damaged Rectangles

With the rectangle splitting routine rewritten and moved into the Rect class, where it should have been from the start, switching to a damaged rectangle-based rendering system starts to become feasible. The last problem to solve is the issue of scrolling. The scroll routines must be working with the latest bitmap data, which means rendering cannot be deferred.

I don’t know how much thought I’ve put into into solving this during the course of Woopsi’s development, but I’d never managed to hit on a solution until a couple of days ago. The answer is painfully obvious once you know it: make any functions that use the scrolling abilities of Woopsi flush any pending damaged rectangle redraws before they start. That way, the screen will be up-to-date before any scrolling is done.

Simple, obvious.

The upshot of all this is that Woopsi’s rendering engine is now based on damaged rectangles. Instead of the TextBox calling redraw() when its text changes, it now calls markRectsDirty(). It sends its list of visible rectangles to a new DisplayManager class (I might change the name of that class at some point), which works out which parts of the damaged rectangles it knows about (and discards them) and which are new (and remembers them). Once every VBL the DisplayManager redraws the display.

Redrawing is achieved by splitting the damaged rectangles into intersections with the gadgets. The DisplayManager performs a depth-first walk of the gadget tree in order to redraw in front-to-back order. Any rectangles that are redrawn are removed from the damaged rects list. Once that list is empty, the redraw method ends.

What has changed?

So, what has changed? There’s no need to call enableDrawing() or redraw() in the startup() method, for a start. In fact, both enableDrawing() and disableDrawing() no longer exist. The redraw() method has gone too, as well as the following:

  • Gadget::erase()
  • Gadget::eraseGadget()
  • Gadget::redrawDirty()
  • Gadget::drawChildren()
  • Gadget::redrawDirtyChildren()
  • Woopsi::eraseRect()

As previously mentioned, these were some of the cludgiest, most unpleasant functions in the system. I’m glad to have chucked them out.

There’s always a Catch

Unfortunately, the hack that the DimmedScreen class exploited no longer works. That class has had to be deleted.

Woopsi 0.99.4 Released

September 2nd, 2010 Ant 5 comments

Woopsi 0.99.4 is now out, but in a break from tradition, you can’t download it from SourceForge. As I said in the last post, Woopsi development has moved to BitBucket.

At Lakedaemon’s suggestion, I’ve created a new website for Woopsi. All releases - including this one - will be available there from now on. The new site can be found here:

This release mainly fixes bugs. Additionally, I’ve fiddled with the fonts and font generation tools a little, mainly to accommodate basic functionality required for Lakedaemon’s “XmlBox” gadget.

The woopsi.org website’s source code is itself hosted by BitBucket, and can be found here:

However, there’s very little PHP behind this site, so there’s not much to see that you can’t find by looking that the HTML behind the pages.

Farewell to SourceForge

August 26th, 2010 Ant No comments

SourceForge has been Woopsi’s source code host since October 2007. They’ve been a great host. They offer some excellent features, and haven’t charged me a thing for nearly 3 years of diligently caring for my code. However, both technology and “social” coding techniques have improved since I adopted Subversion. I’ve found modern distributed version control systems to be faster and more flexible than Subversion, whilst the forking capabilities provided by sites such as GitHub and BitBucket make collaborating with other coders incredibly easy.

A request from Lakedaemon that I move Woopsi to a DVCS finally convinced me that it was time to move on, so I’ve switched from Subversion to Mercurial. Woopsi and its associated sub projects are now hosted on BitBucket:

The SourceForge page remains in place as it’s impossible to close a SourceForge site that has code committed to it. It now includes a notice indicating that the project has moved.

Woopsi 0.99.3: A Belated Release Announcement

August 26th, 2010 Ant No comments

Woopsi 0.99.3 was released a week ago:

http://www.sourceforge.net/projects/woopsi

This is what I said about it on the GBADEV forum:

This release fixes a number of bugs. Some of the code has been tidied up. The only change that could have an impact on user code is the removal of drawHorizLine(), drawVertLine(), drawCircle() and drawFilledCircle() from the GraphicsPort class. The drawLine(), drawEllipse() and drawFilledEllipse() methods will now automatically call the optimised form if it is available.

The full list of changes is available in the ChangeLog.txt file within the archive.

Categories: Woopsi Tags: , ,

WoopsiGfx: A 2D Graphics Library

July 28th, 2010 Ant No comments

WoopsiGfx is a C++ 2D graphics library for the Nintendo DS, derived from Woopsi. It allows developers to create and manipulate bitmaps using a comprehensive set of drawing tools. It includes an extensible font system for drawing text to bitmaps, and features support for packed monochrome and 16-bit fonts out of the box.

WoopsiGfx can be used to draw directly to the DS’ VRAM. This is useful when the DS is in MODE_FB0 or MODE_5_2D.

Features

  • Extensible font system that supports compressed proportional and fixed-width fonts (monochrome and 16-bit);
  • Animation class with support for variable framerates and standard/pingpong looping;
  • Bitmap class for 16-bit bitmap image manipulation;
  • Graphics class providing clipped, DMA-accelerated drawing functions;
  • Dynamic array container and iterator classes;
  • Object-orientated design for easy integration into other C++ software;
  • Simple API;
  • Unicode strings encoded with UTF-8;
  • Compatible with Woopsi font tools.

You can download a demo here:

http://bitbucket.org/ant512/woopsigfx/downloads/woopsigfx-demo-1.00.zip

The source is available as a zip here:

http://bitbucket.org/ant512/woopsigfx/downloads/woopsigfx-src-1.00.zip

Alternatively, you can pull down the Mercurial sourcecode repository from here:

http://bitbucket.org/ant512/woopsigfx

Categories: Woopsi Tags: , , , ,

Removing Code

July 25th, 2010 Ant No comments

I promise I’m not stealing the subject matter for today’s posts from Hacker News. This story got mentioned there today, but it’s something that I’ve kept in the back of my mind when coding for years.

It was this story that lead to the latest Woopsi changes. I’ve been trawling through the code trying to get rid of things that no longer serve any purpose or just clutter things up.

The first change is the removal of the FixedWidthFontBase class. It was needed by the Font and MonoFont classes, which were themselves removed in the last release. Other than in those two classes the FixedWidthFontBase class wasn’t used anywhere. It’s now in the “extras” folder in the SVN repository.

I’ve tidied up some of the Graphics class. The drawHorizLine() and drawVertLine() methods are now protected, and the drawLine() method will automatically detect if a horizontal or vertical line is being drawn and will call the faster method appropriately. All the benefits of the faster routines remain, but Woopsi now handles calling the right method without any developer intervention. The drawCircle() and drawFilledCircle() are similarly called automatically by the drawEllipse() and drawFilledEllipse() methods. These changes meant that the drawVertLine(), drawHorizLine(), drawCircle() and drawFilledCircle() methods in the GraphicsPort class were unnecessary and have been removed.

The putSDLPixel() and getSDLPixel() methods have been moved into the FrameBuffer class as they aren’t used anywhere else.

Finally, the TinyFont class is broken in Woopsi 0.99.2. That has been fixed.

Categories: Woopsi Tags: ,

Software Patents

July 25th, 2010 Ant No comments

Here’s a remarkably apt bit of news for this blog:

Apparently Commodore-Amiga owed $10M for patent infringement. Because of that, the US government wouldn’t allow any CD-32’s into the USA. And because of that, the Phillipines factory seized all of the CD-32’s that had been manufactured to cover unpaid expenses. And that was the end.

http://xcssa.org/pipermail/xcssa/2005-February/002587.html

That’s an unusual assertion: Commodore brought down due to a patent infringement? Their downfall is typically - and, in my opinion, more accurately - attributed to their complete misunderstanding of marketing and the Amiga computer. So what was the patent they infringed?

So, the thing that finally brought the original Amiga house down was the XOR patent! The XOR patent covers the use of the machine language XOR (exclusive-or) operator to make a cursor blink in a bitmapped display.

The XOR patent, eh? Using XOR to draw a cursor to the screen. Must be a fantastically inventive idea, that. Not something that the average programmer wouldn’t come up with in a few seconds of the top of his head. No way.

Here’s a quote from one of my Woopsi blog posts back in October 2008:

The cursor isn’t drawn yet, as it’s a bit tricky. A rectangular XOR box the size and width of the character at the cursor position is probably the easiest option.

http://ant.simianzombie.com/?p=440

That’s a throwaway comment in a throwaway paragraph in a post about creating a timer gadget. The idea is so mind-numbingly trivial that even a decidedly average programmer thinks it barely worth mentioning. Yet this single, simple idea is blamed for the downfall of a multi-million dollar company.

Dear America,

Your patent system is screwed.

Sincerely,

ant.simianzombie.com

Woopsi 0.99.2 Released

July 20th, 2010 Ant 1 comment

Woopsi 0.99.2 is now out. Grab it from SourceForge, as usual:

http://www.sourceforge.com/projects/woopsi

The font system has had some minor changes made. I finally got around to ripping out the Font and MonoFont classes, as the PackedFont classes perform the same job with far more efficiency. They’ve been moved to the “extras” directory in the SVN repository in case anyone should ever need a reference for creating new font classes.

All of the monochrome fonts that ship as part of the library are now PackedFont1 classes instead of PackedFont16. This highlighted a bug in the bmp2font .NET program that is included in Woopsi’s “tools” directory - it was allocating relatively huge bitmaps for the font data instead of the tiny bitmaps that were really necessary. I’ve deleted the “CourierMono” font as it was an exact duplicate of the “Courier” font. The GlyphFont is a PackedFont16-based class instead of a Font-based class.

This changes have reduced Woopsi’s footprint quite significantly:

  • 1.5MB from the size of the Woopsi library;
  • 2MB from the sourcecode;
  • 400K from the packedfont example ROM;
  • 60K from the helloworld example ROM.

A big saving for very little work.

Other changes include replacing the x, y, width and height members of the Gadget class with an instance of the Rect class (itself converted from a struct). A lot of the clipping work can now done be within the rect instance rather than throughout the codebase.

The FileListBox and FileRequester classes include a getPath() method that allows a developer to determine which directory the file requester is pointing at.

Other miscellaneous changes are detailed in the changelog.

Categories: Woopsi Tags: , , , ,

Woopsi Version 0.99.1 Released

May 24th, 2010 Ant 9 comments

This version fixes bugs in the slider and scrollbar gadgets. I basically threw away the previous code and started again. This reworking has introduced a new issue: the grip does not have a minimum size. On the positive side, the sliders behave much more predictably now.

Get it from SourceForge, as always:

http://www.sourceforge.net/projects/woopsi

Categories: Woopsi Tags: , ,