SuperBitmap Manipulation

One minor change today that’s worked as expected - key events are now passed to the active window exclusively. That’s the whole point of having an active window, really, so the previous method of passing the events to all windows doesn’t make much sense.

Another gadget idea. The Amiga windowing system had “superbitmap” windows, which were standard windows with a bitmap buffered in RAM. This buffer bitmap could be any size (memory permitting), and the window it was attached to functioned as a viewport into it. Add scrollbars to the window and you had a canvas that was potentially much larger than the window displaying it.

Seemed like a good addition for Woopsi, so I started working on it. The easiest way to handle the bitmap buffer is just to reserve a block of unsigned shorts in memory, then use the DMA hardware to copy it into the relevant window in order to display it. Scrolling can be handled in the standard handleDrag() event.

Trying to get the SuperBitmap code working really made me wish I’d gone for a 16-bit display instead of 8-bit. Copying data from the bitmap buffer to the gadget was incredibly complicated. The two-pixels-per-memory-address system caused the same complications with this routine as it did with the filled rectangle code, but it added a whole new level of complexity.

In the rectangle code, we have to align the DMA copier to an even starting address and an even length of data, then fill in any gaps once the copier has done its magic. Tricky. In the SuperBitmap code, we’ve got two rectangles to align. Firstly, we need to make sure that the destination rectangle (the gadget) is aligned properly. Secondly, we need to make sure that the source rectangle (the area of the bitmap we want to use) is also aligned properly. This gets stupidly complicated.

Consider this situation - we want to copy a piece of the buffered bitmap to a gadget of width 10 pixels. The gadget is at location (1,2) on the DS’ screen, and the piece of the bitmap we’re copying is at location (6,2). First of all, we need to align the destination rectangle. Pixel 1 gets increased to pixel 2, and to compensate the width is dropped to 9 pixels. 9 is then aligned to 8, giving us a rectangle starting at pixel 2 with a width of 8 pixels. We need to fill in pixels 1 and 10 once the copier has run.

Next we need to align the source rectangle. As we’ve shifted the destination rectangle one pixel to the right, we need to do the same to the source rectangle. This gives us a rectangle at pixel 7 on the buffered bitmap. We need to adjust the width too, so our rectangle is now 8 pixels wide. At this stage, we’ve come unstuck - our source rectangle, which was originally even-aligned, is now odd-aligned. The problem is that we can never get these two rectangles in sync - we can’t even-align the source because that will force the destination back into odd-alignment.

I eventually realised that what I was trying to do was copy the high byte of one 16-bit value into the low byte of another using a 16-bit copy, which clearly makes no sense. The DMA hardware just won’t let me do it.

I’ve come up with a solution (that needs some tidying up) that scrolls the bitmap two pixels at a time. Not great, but it’s the only possible solution that doesn’t involve switching to a 16-bit display. The problem with the solution is that it won’t work on the real DS hardware, but works fine in both of my test emulators.

At this point I have a stonking headache, so have decided to give up for the rest of the day.