When attempting to run 1000 miles, we soon noticed that it *could* draw to its windows, but it didn’t because we didn’t send update events to the application. As Window Manager implementation had already added foundation for posting the update events, we added support for it in the EventAvail/GetNextEvent, and stuff started happening.
After adding BeginUpdate, EndUpdate, and a bunch of other traps 1000 miles needed, such as PlotIcon and Pack7 (DecStr68K) package (for NumToString through LBin2Dec), we got nice looking interface of the game visible:
Update Events working; 1000 miles windows have now content
After the recent changes, we decided to try out another classic game, Tetris:
The sound was not yet working correctly, but at least the title screen appeared correctly. As the game wanted to use ScrollRect to scroll text in the small box at bottom, this gave us a good excuse to go on and implement it:
After some work on the virtual MBDF code resource, and related Menu Manager routines, we now have menu titles visible on the menu bar:
The menu titles are for the first time visible on the menu bar
For testing purposes, we also switched the desktop pattern by adjusting the ‘PAT ‘ 16 resource.
Window Manager
At this point, we wanted to get work done on the actual Window Manager routines, so we chose a few of the most simple applications we had for guiding our development and testing the results. One of them was the really old “3-D Maze” app by Mark Frohnmayer. Having the Pascal source code for it, it was quite easy to trace what the 68K code in it was attempting to do, so we could handle any issues we encountered. As this application is not a full game, but rather a demonstration for a Dungeon Keeper/Wizardry style engine, it consisted mostly of DrawPicture and CopyBits calls into a window.
Even the first GetNewWindow call had a lot of dependencies to other Window Manager traps (not to mention the ShowWindow and SelectWindow later!), but we got soon the first window displayed on the screen:
The first ever window, with picture drawn into it by the 68K test app
As we focused on the actual Window Manager implementation, we left the default WDEF for now as a simple one-pixel frame (and added for testing regions a small drop shadow), the windows look quite plain at the moment, but that should be enough to get applications run further.
1000 miles creates a lot of windows!
We also tested the new Window Manager routines with another app, 1000 miles, which was quite interesting because unlike most applications, which usually have one or a few windows, it had literally a window for every possible part of it’s user interface.
As one of the main goals is to actually run our favorite games in this emulator, we started now converting some of them into the AppleDouble format, and one of the was the legandary Dark Castle by Silicon Beach Software. It almost worked:
Well, theres something there…but not quite right yet
After fixing a bunch of bugs, and adding another bunch of new traps, it actually didn’t take long to get the title screen to appear. However, GetNextEvent was unimplemented at this point, so couldn’t yet proceed past it, and also the animation timing appears to still a bit off. It was nice though to see that the alternate video page switching code appears to work nicely, including also for the first time having 68K code running in VBL service thread! The video below shows this running:
Now that the test application is passing its runtime startup, it’s attempting to initialize various Toolbox managers, which of course gives us a good excuse to start work on the Menu and Window Managers, beginning from InitWindows.
The menu bar and desktop are some of the most iconic parts of the Mac user interface, and as these initialization routines set up these, we have something that is finally stating to look like a real Mac desktop. Another benefit of having file system at this point is that we can actually read the desktop pattern from ‘PAT ‘ resource ID 16 (using new GetPattern trap) from System file, like a real Mac does, and use that to paint the desktop.
First appearance of the menu bar
Another interesting new feature was the support of virtual code resources read from the System file, using a customized mixed-mode loading mechanism which allows inserting UPP record for a native C code resource, such as in this case MBDF, into the MBDF resource read from the System file – which in this case was used to draw the menu bar!
Now that the source transfer modes were added for drawing text, a logical next step was adding support for drawing the most basic bitmap-only PICT resources. For this purpose, we added a very simple test PICT resource into the Fake ROM resources, which we used to test this feature.
More than a bitmap
What makes the basic QuickDraw PICT format different from many other image formats is the fact, that it is actually not a pure bitmap container, but rather a recording of drawing commands, which can be played back at any time. Basically this allows majority of QuickDraw commands to be “recorded” into a picture, and later played back. Even better, these pictures can be “played” also on other devices besides screen, such as printers! This allows a picture with geometric shapes to have a rasterized on-screen representation, but appear as a sharp image on for example a laser printer. Furthermore, these pictures can be scaled during playback to a target rectangle, allowing them to adapt to different screen/device sizes as appropriate. Of course, all this makes displaying a simple scanned bitmap photo of a cat a bit more complex than one would expect.
A simple PICT image
Attempting to create the monochrome old-style PICT resource was a bit challenging on modern Macs, so we ended up transferring the photo on the reliable good old PowerBook G3, and exporting to resource from there using old version of PhotoShop.
Playback time!
So basically what we needed to display the cat picture, was just take the PICT resource and do the following:
Read the picture size and picFrame from the header
Save copy of content of current GrafPort
Set up defaults for the GrafPort (including new clipping region)
Initialize a playback state record
Execute each picture opCode from picture data one by one
Dispose playback state and restore original GrafPort state
Even in this simple format, the following opcodes were required for the test PICT: Version, LongComment, PackBitsRect, and EndOfPicture.
Type 1 picture opcodes encountered in the cat PICT
All other opcodes were trivial to implement, except PackBitsRect which ended up using UnpackBits trap, so we had good excuse for implementing it also at this point. With unpacking implemented, the drawing ended up with a simple call to StdBits, which was was already previously added for text drawing, and we got the cat quickly on the screen:
First appearance of the cat PICT
As clipping was already implemented, we were able to goof around by drawing the PICT in funky clipping region (with some text added for fun):
Cat PICT drawn masked by another, larger region, which was later framed with FrameRgn using different sizes and patterns to create gradient edge
Previously, during the spring, we had added the basic region operations, which already allow us to do a bunch of cool stuff, but next up on the list was implementing region recording. This neat feature QuickDraw allows the program to open a new output region for “recording”, to which certain QuickDraw “FrameXXX” operations will output inversion points into. This includes StdLine, StdOval, StdRect, StdRgn, and RoundRects.
The article on MacGUI, which was mentioned earlier when discussing regions, also contained a neat screenshot of results of OpenRgn/CloseRgn operation with accompanying Pascal source code. We made adaption of this code for the C Toolbox testbed, so we could have a picture to compare to, to see that the results would match ones expected on a real Mac.
Again, first attempt rarely works…
At the same time, we were working on adding “source” transfer mode support to the region-clipping blitter, as transferring text to screen uses those to handle moving the text from offscreen buffer to the screen (previously we only implemented the non-clipped blitter). After getting this to work, there was also a nice benefit that non-scaled CopyBits can also be implemented easily soon, although only for cases where source and destination bitmaps are different (ScrollRect and window manager will use same source and destination, forcing inverted blitting direction depending on their locations).
Region recording works now, as does the text clipping
A few days earlier, we got the non-styled direct-to-screen text drawing working, but we also need to support all the other cases. At this point, we want to have the clipping and styling support, but we’ll leave the scaling to some point in the future.
As styled text required an offscreen buffer for styling, we started by adding that. Interesting feature of QuickDraw FMSwapFont mapping is, that as any styles can be combined together, the FOut record contains a set of styling attributes which tells the text rendering how to adjust the final appearance of the font, such as bolding amount, how much slanted the italic style is, how deep the shadow is, etc.
Well, as usual, the first run didn’t go quite the way it was supposed to
After a few days to debugging, bug-fixing, more debugging, and even more bug-fixing, we go the text to render properly:
The final, correct styles
For reference, styles from PowerBook G3 running Mac OS 9. Note the difference in outlined/shadowed text.
An interesting discovery was, that it appears that “Classic”-type QuickDraw and Color QuickDraw handle the outline styles a bit differently. This may be due to the way outline/shadow styled text is combined during drawing, as the “inside” of outlined text is drawn over the shadow/outline using srcXor mode. Another interesting point is, that even Color QuickDraw falls back to the “old” type drawing if the GrafPort is old-type monochrome port (i.e. high-order bit of rowBytes is zero)
Even more combinations of styles and drawing modes!
As mentioned earlier, the next target is to get QuickDraw text rendering to work. For this purpose, we’ve been working on important part required by it, The Font Manager. The most important interface between QuickDraw and Font Manager is the FMSwapFont trap, which takes the FMInput record, and outputs an FMOutput record. On a real Mac, the font substitution has a bunch of special cases which we luckily don’t need to worry about; for example, we don’t need to worry about choosing right font to display the disk-switch alert. However, there are a number of substitutions we need to support:
Substitute non-present font to default font
Choosing nearest-size font strike to substitute a non-present (scaled) size of a font
FOND tables
Fallback to old FONT resources in case requested FOND does not exist (using the familiar family * 128 + size formula)
Etc.
Getting the strike loaded and on the screen
Majority of time spent for this milestone so far was investigating and implementing the quirks of the previously mentioned FMSwapFont trap. After finding the correct (or near-correct) strike, we finally got a FONT resource which we could work on. Of course, this meant that as first step dump the raw font bitmap on the screen buffer byte-by-byte 🙂
12 pixel “Chicago” font dumped on the screen. Tight fit, huh?
Of course getting the actual text to output, each of those characters would have to be individually masked and blitted to the correct location. For the first test, the original QuickDraw luckily appears to have “optimized” way to blitting non-clipped, non-scaled, non-styled srcOr-mode text directly to the screen, without having to go through offscreen buffer for styling and clipping. Implementing this was also pretty straightforward, resulting in the following tidy output using different fonts:
First successful text output, with various fonts in the fake ROM
After the groundwork done for rectangle drawing, the rest of geometric shapes were pretty easy to add. To make sure the results of drawing operations would look exactly like on a real Mac, the Midpoint circle algorithm was implemented as closely to what Atkinson used as possible
More information about the Midpoint (“Atkinson”) circle algorithm
The fun thing is, that a bunch of QuickDraw primitives actually share this method, so not only did this allow drawing Circles, it also enabled drawing ovals (basically stretched circles, thus this is why overly long framed ovals on Mac tend to have holes on their silhouette), and the infamous round rectangles. Also drawing arcs benefits from this, but we won’t be needing them at least for some time so we won’t touch them at this point.
Ovals, circles, and round rectangles drawn with random pen modes and with random patterns
Here’s also a neat video of round rectangle clipping:
Round rectangles getting clipped in the last week’s region test
You must be logged in to post a comment.