2009-11-10

Bitmaps, Framebuffers, Drawing, and Fonts

The latest set of updates encompasses a substantial amount of fundamental changes within Woopsi. It now includes the following types of bitmap class:

BitmapBase

The most basic bitmap abstract class from which all others inherit. It defines the most fundamental properties of an immutable bitmap.

BitmapWrapper

Extends the BitmapBase with the ability to wrap around a raw u16 array and permits it to be used as a standard bitmap object.

MutableBitmapBase

Extends the BitmapBase with the ability to set the colour of a pixel at a given set of co-ordinates. Can also provide a non-const pointer to the raw u16 array. This is an abstract class from which all mutable bitmaps should inherit.

Bitmap

The standard bitmap object. Can produce a new Graphics object that has the capability of drawing to the Bitmap in the same way that the GraphicsPort draws to a gadget.

FrameBuffer

Almost identical to the Bitmap class, except it accepts a pointer to a pre-existing non-const u16 array in its constructor instead of creating the array internally. Intended as a wrapper for the framebuffer to allow it to be used as a bitmap object.

These changes supercede the changes discussed in the last post.

The drawing functions have been removed from the Bitmap class and split up into a class hierarchy:

GraphicsUnclipped

Contains all of the drawing functions previously stored in the GraphicsPort and Bitmap classes, minus any clipping code. Will draw to any instance of a class that inherits from the mutable bitmap class.

Graphics

Inherits from the GraphicsUnclipped class and adds simple clipping functions that ensure the drawing methods do not exceed the width of the bitmap being drawn to. Typically used when drawing to standard bitmap objects.

GraphicsPort

Inherits from the GraphicsUnclipped class and adds complex clipping functions that ensure the drawing methods do not attempt to draw outside the boundaries of a clipping rect, or array of clipping rects.

These changes mean that the basic drawing functions now only exist in one class, instead of being duplicated (with minor changes) in two places. The rationalisation also means that functions such as dim(), copy() and the XOR drawing methods are now available for use on bitmap objects as well as within gadgets.

In order to achieve this, the DrawBg array that previously allowed access to the framebuffer has been replaced with an array of FrameBuffer objects.

Changing these classes necessitated changes to the font system. All fonts that use unpacked bitmap data now expect to be supplied with a pointer to a bitmap object instead of a raw u16 array. Changing this required a rewrite of the rendering and clipping code in the Font and MonoFont classes.

The two standard fonts, “systemFont” and “tinyFont”, are now available as font global font objects. They are instantiated in the initWoopsiGfxMode() function in woopsifuncs.cpp.

These changes should bring a variety of benefits:

  • Fewer magic numbers flying around as bitmap width/height values are stored within bitmap objects instead of specified as parameters for every method;
  • Font bitmaps can be loaded dynamically from BMP files using BitmapIO;
  • It should be easier to add conditional compilation to the drawing methods in order to optimise SDL screen updates;
  • The drawing functions are no longer duplicated in several classes;
  • Bitmaps and the GraphicsPort have access to the same set of drawing functions.

There are a couple of new issues:

  • Blitting a bitmap to another bitmap seems to result in the occasional set of missing pixels at the start or end of the bitmap, probably resulting from the DMA hardware seeing old data (need to flush);
  • I need to check that the new structure is tidy.

2009-04-22

Happy Belated Birthday, and More Event Stuff

It was the blog’s birthday on April 11th. I’d intended to put up a birthday post like I did last year, and maybe a celebratory Woopsi app, but exam work got in the way. Ah well.

Anyhoo, I made some more Woopsi changes nearly a month ago that I haven’t been able to post about yet, so here’s some info on those.

One of the lingering problems with the Gadget class has been the way it overloads the draw() method. There are two draw() methods, one without parameters, and one with a single parameter (a Gadget::Rect). The first method works out which parts of the gadget are visible, whilst the second actually draws those visible parts. Sounds good, but in practice it causes problems.

There is a bug in GCC (or a weird C++ behaviour; let’s assume it’s a bug in the compiler and not a design flaw in the language) that prevents this arrangement from working. If we create a subclass of gadget (let’s call it “SubGadget”), we want to override the draw(Rect) method so the gadget can define how to draw each visible region. However, we don’t want to override the draw() method, as this is identical for all gadgets. Unfortunately, we can’t override one without also overriding the other. The compiler complains. As a workaround for this I have been overriding draw() with a method that just calls the base method, but this is clunky.

As a proper fix, the draw() method is now called “redraw()”. This change will probably break your code if you have created your own gadgets.

There are a few more changes in the event system. Previously, whenever a selection was made in the context menu, the gadget that “owned” the menu raised an event indicating the selection. It was then the programmer’s responsibility to check the context menu gadget itself to determine which value had been chosen. This is no longer the case - the handleContextMenuSelectionEvent() method now receives a reference to a “ContextMenuEventArgs” object that contains a pointer to the menu item that was selected. Much simpler to work with.

In other changes, the WoopsiArray and LinkedList classes no longer contain the “begin()” methods. This was left over from when Woopsi switched from the STL vector class to the WoopsiArray, a change in which I’d purposely made the WoopsiArray a drop-in replacement for the vector. The “begin()” was just a stub method, though - the WoopsiArray does not implement the STL iterator code, and begin() simply returned a zero. The WoopsiArray uses indexes internally, not pointer arithmetic, to navigate its contents.