Archive

Posts Tagged ‘bugfixes’

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: , ,

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: , ,

Woopsi 0.99 Released

May 20th, 2010 Ant No comments

Woopsi 0.99 is now available from the usual place:

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

This version has had a significant version bump because it is considered complete. I’m holding off from releasing a “version 1.0” to allow more time for any bug reports and for me to finish writing the documentation.

Changes in this version include some minor cosmetic improvements to the ProgressBar, bugfixes to the slider gadgets, and some more gadget test projects. I’ve added in the compiled libfreetype.a file that was missing from all previous releases.

Some preliminary documentation can be found here:

Woopsi Docs 20100520 (PDF)

One of the ongoing problems I’ve had with Woopsi’s documentation is that it becomes obsolete as soon as it is written. Before I wrote this document, for example, I hadn’t added UTF-8 support to the WoopsiString class. Nor had I decided that WoopsiStrings, not char arrays, would be the official way of working with string data. I set about documenting Woopsi’s string support assuming that they were too fundamental to change, and then had to re-write the entire string chapter once the UTF-8 changes were in place.

The document above is at a very early stage. Comments and criticisms are welcome. Once the documentation is finished, it should hopefully be both a complete guide to developing with Woopsi and a description of how the library works:

  • What to install to get it running, ie. devKitARM, etc (installation instructions for 3rd-party components will just include links back to their sites);
  • How to create user interfaces;
  • How to create new gadgets;
  • The internal workings of Woopsi (co-ordinate systems, gadget painting algorithms, etc).

Now that Woopsi is essentially finished, here are some interesting Woopsi stats:

  • Over 91,000 lines of code;
  • 600MB for a full repository checkout;
  • Source distribution is 37MB when unarchived;
  • 5 other contributors;
  • 9MB of Doxygen-generated documentation;
  • 399KB for a “Hello World” app (twice the size I’d hoped for, but WPaint - a simple paint program - is only 397KB);
  • Development spanned 2 years 8 months, though for at least half of that time no development was undertaken;
  • 5 released projects using Woopsi:
  • 2 unreleased projects that may be using Woopsi:

Judging by the Google search terms collected by this blog, I know that there are many more projects out there using Woopsi. At the very least, there are plenty of people who try to use Woopsi but balk at the lack of documentation. Strangely, they don’t post on the forum for assistance.

Whilst on the subject of stats, here are some from the blog itself:

  • 367 posts;
  • 132171 words in posts;
  • 859 comments;
  • 100834 words in comments;
  • 37 months blogging.

That works out as roughly 120 words a day for 3 years.

Categories: Woopsi Tags: , , ,

Woopsi 0.46 Beta Released

March 9th, 2010 Ant 5 comments

Yet another release. This one is special as it marks Woopsi’s transition from alpha to beta. Woopsi is now in beta, which means that the feature set is frozen and I’m going to concentrate on documentation and bug fixing.

Woopsi is the poster child for the dangers of feature creep. I started it back in September 2007 with the idea that I’d support screens, windows, buttons and a textbox, and get the whole thing written in a month. Two and a half years later, Woopsi has evolved into a fairly complete windowing system with scrollers, listboxes, UTF-8 encoded unicode strings, TrueType font support, a suite of clipped 2D drawing tools, etc. A full SVN repository checkout consumes over 500MB of disk space.

I’m finally happy with the feature set. There are, of course, more features I’d like to add, such as skinning and bitmap loading, but it’s reached the point that I’d prefer to stop adding to the code and document what I’ve got so far.

I’m also very interested in feedback about the library. If you’ve been tempted to use Woopsi but have been put off by the ever-changing API, now is a good time to get started with it. The more feedback I get, the more bugs I’ll be able to fix.

Once all of the documentation is done and I hit version 1.0, I’m considering talking to some of the homebrew websites to see if they’d be interested in running a Woopsi-based competition. Just an idea at the moment, and I’m not sure how practical it will be or if they will be interested. Still, worth a try.

This latest release includes the last set of new features. MultiLineTextBoxes implement the cursor fully. The cursor can be moved up, down, left or right with the d-pad. It moves to the cursor’s position when the textbox is tapped with the stylus. The textbox scrolls to follow the cursor when it is moved. The MultiLineTextBox has been significantly refactored. All of its alignment options finally work, and it even shows the keyboard when it is double-clicked (though this behaviour can be disabled via a new disableKeyboardPopup() method).

I’ve added a few base classes that define the interface for some of the gadgets, enabling composite gadgets that mimic their behaviour to present a more consistent set of methods.

There are a few other bugfixes and improvements; they are all listed in the changelog, as usual.

Download the new release from SourceForge:

GraphicsPort Refactored

February 19th, 2010 Ant No comments

There have been quite a few changes and improvements since the last blog post. The biggest one is another redesign of the GraphicsPort class. Back in November last year I stripped the drawing code out of the bitmap class and separated it into a new hierarchy. The GraphicsUnclipped class contained drawing methods that were not clipped. The Graphics class was a subclass of GraphicsUnclipped and clipped to the confines of a bitmap. The GraphicsPort also subclasses GraphicsUnclipped and clipped to the visible regions of a Gadget.

This seemed like a reasonable design, but it did have its downsides. As the Graphics and GraphicsPort classes were peers within the inheritance hierarchy, rather than one subclassing the other, there was inevitably some repeated code. More disturbing was the spaghetti-like integration between the classes, in which GraphicsPort would call a method in GraphicsUnclipped which would in turn call a method in GraphicsPort.

In addition to the inheritance problems, the GraphicsPort had its own internal problems. Its drawing methods used a variety of different co-ordinate systems. Some used the co-ordinates of the entire UI as their reference point, whilst some used the co-ordinates of the gadget, and others used the co-ordinates of the GraphicsPort itself.

The GraphicsPort is designed to work in two ways - it can be constructed and used within a gadget’s draw methods, or it can be created outside of the gadget and used to draw over the gadget. This meant that it needed to retain either a single clipping rectangle, used when drawing from within a draw method, or a list of clipping rectangles, uesd when drawing from outside of the gadget’s code.

All of these problems led to a horrible mess, which I have now put right. I’ve even introduced some new features in the process.

First of all, I’ve merged the Graphics and GraphicsUnclipped classes into a single class: “Graphics”. It contains all of the drawing methods available to the entire Woopsi system. The class can be given a clipping rectangle in which it can draw, which means that all of the drawing methods now clip. Any potential speed loss is negligible, since the drawing methods were already clipping to the confines of the bitmap being drawn to. I’ve just made that clipping area user-definable.

The GraphicsPort class no longer inherits from either of the other Graphics classes. Instead, it includes an instance of the Graphics class and presents a facade over the top. The GraphicsPort no longer has a convoluted set of responsibilities; it now:

  • Maintains a list of clipping rects for the gadget it relates to;
  • Receives drawing instructions;
  • Converts the co-ordinates from “GraphicsPort space” to framebuffer space;
  • Uses its Graphics object to draw to all clipping rects in its list.

The GraphicsPort does not maintain a single clipping rect in addition to a separate list of clipping rects. It now adds that single clipping rect to its list, erasing any previous data in its list. In this way, it achieves the same functionality without the extra complexity of two separate data storage mechanisms.

I mentioned GraphicsPort space and framebuffer space in the above descrition. I’ve put some work into trying to formalise the different co-ordinate systems that Woopsi uses. Descriptions of these will be included in the documentation whenever I get around to finishing it.

The GraphicsPort class is now entirely separate from the Gadget class. Previously the GraphicsPort included a pointer to the gadget that it was drawing to. This is no longer necessary, which should result in a (negligible) speed increase, since the GraphicsPort no longer needs to query the gadget’s Woopsi space co-ordinates using its recursive getX() and getY() methods.

I’ve removed the OutlineType enum from the Graphics class. This was not relevant to all gadgets so should not have been in the base class. This resulted in the addition of a new CalendarDayButton class and some changes to the WoopsiKey class so that they could remain “stuck down” when selected, which is represented by their outlines changing from bevelled out of the screen to bevelled into the screen.

I removed the padding variables from a few classes a week or so ago. I’ve now done the same to the MultiLineTextBox and replaced it with larger border sizes. This change, coupled with the rationalisation of the GraphicsPort’s co-ordinates, finally enabled me to identify the bug that caused the textbox to have graphical glitches when using a padding of greater than 12. That’s the first bug I’ve closed in the SourceForge tracker in months. The textbox’s vertical top alignment option works correctly, too.

Lastly, I’ve improved the cursor-following code in the TextBox again. When deleting characters from a string that is wider than the textbox, it is no longer possible to create a large gap between the end of the string and the right edge of the textbox.

Simplifying the API Part 5

February 13th, 2010 Ant No comments

More tidying up and throwing away. A while ago I altered the TextBox so that it would scroll to follow the cursor. The code worked but it was ugly, and the scrolling effect was ugly too. The text was always aligned to the left of the textbox, so if the cursor scrolled to the right partial characters would appear on the right-hand side of the box. What a user would expect to happen is see the characters on the right aligned nicely whilst the characters on the left were partially visible. I ripped out the existing code and replaced it with a much improved system.

I’ve removed the “_padding” variable from the Label, Requester, FileRequester and Alert classes. This variable was used to force the gadgets’ children to leave a border between themselves and the real gadget border. I’ve replaced it with the newly improved border code. Related to this, a number of gadgets have improved and simplified drawing routines.

Finally, I’ve added a new set of XOR drawing routines. These accept a colour parameter to XOR against, allowing for more interesting XOR effects.

Categories: Woopsi Tags: , , ,

Simplifying the API Part 4

February 11th, 2010 Ant No comments

The basic Woopsi gadget includes the concept of a border. This is one pixel wide and surrounds the entire gadget. It can have one of a variety of appearances, such as bevelled in, bevelled out, bevelled depending on whether or not the gadget is clicked, and so on. Other gadgets, such as the AmigaWindow and TextBox, have wider borders. The AmigaWindow is particularly unusual because its top border has a different size to the other borders. It eschews the standard border appearance, opting for a border built out of other gadgets instead.

Up until now, the border system has not been implemented very well. Most of the gadget calculate the amount of space taken up by their border using something like this:

u8 width = (!_flags.borderless) << 1;

If the gadget has a border, this evaluates to “2”. If not, it evaluates to “0”. Efficient, yes. Maintainable, no.

Instead, the Gadget class now includes a “GadgetBorderSize” struct, that contains individual sizes for each of the four borders. All of the hacky bitshifting code has been removed and replaced with calculations based on the new struct. This has allowed me to remove all custom “getClientRect()” methods from all gadgets and make the method in the base class non-virtual.

Next, I have removed the “bonus” folder, which contains the skinned gadgets, bitmap I/O classes, hashmap and linked list classes, out of the Woopsi section of the SVN repository and into a new section called “extras”. I’ve decided not to include these in any future distributions, at least until Woopsi 1.0 is out. Too much scope creep will prevent me from ever finishing this library.

To trim some of the flab from the API, I’ve been cutting unused functionality throughout the codebase. The following methods have been removed:

  • Gadget::clear()
  • Gadget::clear(clipRect)
  • Gadget::newInternalGraphicsPort(isForeground)
  • AmigaWindow::getBorderSize()
  • Screen::getBorderSize()
  • TextBox::getBorderSize()
  • Screen::getTitleHeight()
  • Window::getTitleHeight()
  • Gadget::getBackgroundRegions()
  • Woopsi::closeChild()
  • Woopsi::shelveChild()
  • Woopsi::release()
  • Woopsi::drag()
  • Woopsi::click()
  • Woopsi::shiftClick()
  • Woopsi::shelve()

Many more of the Gadget methods that used to be virtual cannot be overridden any more. This prevents essential pieces of functionalty being replaced.

The GraphicsPort has seen a few changes. It no longer tries to delete a null pointer if its clipRect has not been allocated. More interestingly, the gadgets use the GraphicsPort in a different way. Previously, a gadget would define the code to be called when it was to be drawn like this:

void draw(Rect clipRect) {
    GraphicsPort* port = newGraphicsPort(clipRect);
    port->drawFilledRect(0, 0, _width, _height, woopsiRGB(31, 31, 31));
    delete port;
}

Behind the scenes, this was very inefficient. A GraphicsPort object was created and deleted every time this method was called. As this method is called for every visible region of the gadget (and there could be dozens of visible regions), GraphicsPort objects were being created and deleted far too often.

The draw() method has been replaced with two new methods: drawBorder(), which can draw across the entire visible surface of a gadget (including its border space, hence the name) and drawContents(), which can only draw inside the border. The code above would look like this in the new model:

void drawBorder(GraphicsPort* port) {
    port->drawFilledRect(0, 0, _width, _height, woopsiRGB(31, 31, 31));
}

In the new design, two GraphicsPorts are created every time the gadget is redrawn. One can draw to the border area, whilst the other can only draw within that area. They are re-used for every region of the gadget drawn during that redraw operation.

It is still possible to get the clipping region by calling the GraphicsPort’s new “getClipRect()’ method.

The Gadget class’ GadgetStyle object used to be allocated using “new” and referenced with a pointer; it’s now allocated as a standard object. There’s no real reason for this change other than it’s a little tidier.

There were a number of fiddly problems with the Gadget::closeChild() and shelveChild() methods, which are now fixed. They were causing the focus to jump between objects even if the closed/shelved gadget did not have focus.

Lastly, I hit a problem with the shift-click code from yesterday. The clicks were allowed to propagate back up the hierarchy, which caused nasty problems. If the front-most screen did not contain a context menu, the screen below it would be sent the shift-click. If this screen had a context menu then it would be displayed despite the screen itself being totally obscured by the screen above it. I’ve added a new “checkCollisionWithForegroundRects()” method to the Gadget class that fixes this problem. It checks to see if the click falls within one of the regions of the gadget that is not obscured by its siblings or ancestors. If not, the shift-click is ignored.

Categories: Woopsi Tags: , ,

A Bugfix Bonanza

January 19th, 2010 Ant No comments

If you have been following the comments for the previous few posts, or the forum, you’ll have seen work progressing at a frantic pace. Most of the thanks for this goes to Lakedaemon, who has been zapping bugs throughout Woopsi’s text rendering system. Working so quickly and not regularly blogging about it means that I’ve probably got a lot of changes to list here. Let’s see if I can collate them into some sort of order…

The WoopsiString class has seen a lot of changes. I’ve added indexOf(), lastIndexOf() and subString() methods. The first will find the first index of a particular character in the string and return its index. The second does the same thing, but in reverse - it finds the last index of a character. The last will return a pointer to a WoopsiString containing a subsection of the original string.

WoopsiStrings are no longer null-terminated. They already included a member variable that stores the string length, so appending a terminator was a waste of a byte.

The Text class’ wrap() function has had some long-standing bugs fixes. The strangest was probably an invalid comparison between a line index and a char index that was supposed to prevent the function from wrapping the entire text, but actually just screwed up the function most of the time, and made it slower the rest of the time. Not sure how that stayed in there for so long.

The next most significant bugfix was to the GraphicsPort’s clipScroll() method. This is used primarily in the ScrollingPanel gadget. It performs two functions. If it is possible to copy any of the existing visible region to the new location, the function copies it. This prevents redrawing, which is slow, if the required visual data is already available. Secondly, it remembers any rectangular regions that require redrawing and passes the list back to its caller. The ScrollingPanel uses the list to redraw any non-copied regions of itself.

The clipScroll() method worked perfectly if scrolling in just one plane, but as soon as horizontal and vertical movement was attempted simultaneously, it fell apart. Rectangular regions were missed out or copied to the wrong locations, particularly in the corners.

The function was originally tackling the problem backwards. It calculated the size of the source and destination rectangles then clipped those rectangles. However, this introduces two problems:

  • The rectangles could become different sizes;
  • The rectangles could acquire different offsets from their original locations.

Neither problem is too difficult to solve, but it does mean that if either problem arises, the function will not be able to copy the regions and must therefore fall back on the much slower redraw option.

The “correct” approach to the problem is to clip before calculating the sizes of the source and destination rectangles. That will ensure that neither of the two highlighted issues arises, which means that the copy can be used in preference to redrawing much more frequently.

I’ve finally got around to removing the glyphs from the Topaz and NewTopaz fonts. As the glyphs are now an entirely separate font, keeping that data in the bitmaps was a waste of space. Neither font requires colour now that the glyphs are gone, to I’ve converted Topaz from a Font to a MonoFont, and converted NewTopaz from a PackedFont16 to a PackedFont1. This has made both fonts considerably smaller.

Related to this, PackedFontBase::isCharBlank() returns the correct value (false) if the requested character is outside the range of the available bitmap data.

Dragging a background screen below the level of the topmost screen no longer crashes the system. The Screen class was attempting to copy a region with a negative height, which the DMA hardware really doesn’t like.

The FileRequester has had yet more fixes since the last post. When running in SDL mode, it now shows a dummy list of files and directories. I had intended to get it to show a real list of directories, but I ran into The Windows Problem - every major operating system but Windows has POSIX-compliant file system access. Unfortunately, of those major operating systems Windows is the most prevalent. I really couldn’t be bothered to write the POSIX code if most Woopsi developers are using Windows, and I couldn’t be bothered to write Windows code when I only run the SDL version in OSX. The compromise, which will work on all platforms, POSIX or not, was just to create the dummy list.

Finally, both the MultiLineTextBox and the standard TextBox respond to d-pad repeats. Holding down left or right on the d-pad when either of these two gadgets has focus and a visible cursor will cause the cursor to move repeatedly until the d-pad is released.

Unicode Complete

January 12th, 2010 Ant 12 comments

Woopsi is now 100% unicode. Except for anything in the “bonus” folder, which I’ll get around to at some point.

I’ve tidied up the WoopsiString, removing redundant methods and sorting out which should be public and which shouldn’t. To cater for iteration, rather than trying to redesign the string, I’ve added a new StringIterator class. The WoopsiString can produce instances of these (in the same way that Gadgets produce instances of the GraphicsPort class) that can iterate over the string. It has some cunning optimisations so that it will always choose the optimal way of moving to a new character index.

On top of that, I’ve moved the FileRequester and associated classes into the main codebase. Lakedaemon pointed out that, unless you use any libfat features, it isn’t included in the final ROM. A quick test revealed that this was indeed the case, so there’s really no reason not to enable libfat in the makefile by default and include the FileRequester in the main library.

I’ve fixed a few problems in the FileRequester. Its background was transparent in places, which was caused by one of the gadgets in its children not having a draw() method. The sorting was broken - I thought I’d fixed this - so although the items were sorted alphabetically, directories were intermingled with files. Lastly, it doesn’t try to draw before the rest of Woopsi has been drawn, a problem which caused some graphical corruption.

The Bmp2Font .NET utility for converting BMP files to Woopsi fonts now creates working Font subclasses. I’ve removed four fonts from Woopsi as they didn’t seem to be converted properly by Jeff’s original process - they either had glyphs with missing sections or the vertical positioning of some of the glyphs was off.

Unicode News

January 5th, 2010 Ant 1 comment

Unicode support in Woopsi is coming along nicely. Most of the gadgets now expect to receive WoopsiString objects instead of chars or char arrays when dealing with text. As the WoopsiString now works with UTF-8, that means most of the gadgets now also support UTF-8.

I’ve overloaded the WoopsiString’s “=” operator for WoopsiString objects, char arrays and u32s, which means that any parameter that should be a WoopsiString can accept char arrays or u32s instead. Converting from string literals to WoopsiStrings is therefore seamless and automatic.

The glyphs are now stored in a separate “GlyphFont” font. The default glyph font is stored in the defaultGadgetStyle global variable, in the same way that the default system font is stored. Converting this unearthed a couple of bugs in the Bmp2Font program - it doesn’t write out the resultant font constuctor properly (it misses out the transparent colour and uses the raw bitmap data instead of the bitmap wrapper object). I still need to fix those.

The most problematic changes were convincing buttons that displayed glyphs to centre their text properly (turns out that they were centring based on the wrong font) and getting the MultiLineTextBox to redraw at a reasonable speed. After some cunning hacking about with the WoopsiString and the MultiLineTextBox I’ve got it back up to something approaching its old speed.

Regarding speed, unicode definitely has a negative impact. Having to scan through an entire string to locate a codepoint is a horrible performance killer. WoopsiString::getCharAt() is definitely not a function you should use if you’re reading out sequential characters, as it looks for the codepoint from the start of the string each time it is called. It’s much more advisable to use getToken() to get a pointer to the first token, read out the codepoint, then increase the pointer by the size of each character as it is read. Before I went back to optimise things, the MultiLineTextBox was using getCharAt() when drawing and so was the PackedFontBase, meaning the string was being scanned through twice for each character being printed. Xcode’s “Shark” profiling utility noted that the MultiLineTextBox was using 31% of the total CPU time when printing a line of text solely because of all the UTF-8 parsing. Yikes.

There are still a lot of gadgets to convert over, and probably a hefty set of bugfixes and optimisations to make.