Offscreen rendering
If you start doing anything meaningful with QuickDraw, you will eventually run up against the frustration of screen flicker when you try to animate something.
This is easy enough to solve with offscreen rendering (once you dig up the right info on how the hell to do it). There are a few ways to achieve offscreen rendering, but the easiest is via GWorlds.
Assumes you’ve already set up an app with a window, event handling, etc.
Initializing
I find it more convenient to just store the Window, any GWorlds and their related data in globals because I’ve only really been writing toy apps so far, may need a rethink for more elaborate apps.
WindowPtr global_window;
CGrafPtr global_mainPort;
GDHandle global_mainDevice;
GWorldPtr global_gWorld;
These are the 4 main variables we’ll care about, global_window
being the main window, global_mainPort
and global_mainDevice
are the graphics port and device of the main window respectively. Finally global_gWorld
will hold the buffers and so on to allow us to do offscreen rendering.
Assign them as such, I do this in an Initialize
function (after the usual Toolbox init stuff):
void Initialize(void) {
QDErr creationError;
Rect rect;
// Toolbox init goes here first, before you create the window, etc
// Create a new color capable window and store its
// graphics port and device for later
SetRect(&rect, 12, 48, 512, 384);
global_window = NewCWindow(
nil, &rect, "\pMy Cool App", true,
noGrowDocProc, (WindowPtr) -1, true, 0);
SetPort(global_window);
GetGWorld(&global_mainPort, &global_mainDevice);
// Set up a GWorld for offscreen rendering
creationError = NewGWorld(
&global_gWorld, 0, &global_window->portRect,
nil, global_mainDevice, noNewDevice
);
// Checking this error is important as it's the most likely thing to fail
if (creationError) {
SysBeep(50);
ExitToShell();
}
}
I mentioned that creating the GWorld is the most likely thing to fail, this is what wasted a lot of my time until I worked out I needed to increase the available heap size of my application to store the buffers required for the GWorld (32-bit colour is expensive!).
See this article for how to set your applications minimum and preferred memory size.
Drawing offscreen and blitting
I do my drawing in an Update
function, after handling any user input and animations:
void Update(void) {
PixMapHandle offscreenPixels;
// Handle user input and update animations first
// Get the handle to our offscreen pixmap
offscreenPixels = GetGWorldPixMap(global_gWorld);
// Lock the buffer so the memory manager doesnt move it while we are
// trying to draw to it.
if (LockPixels(offscreenPixels)) {
// Set the current GWorld to our offscreen one
SetGWorld(global_gWorld, nil);
// Do offscreen rendering here
// Restore the current GWorld to our main window
SetGWorld(global_mainPort, global_mainDevice);
// Copy the offscreen buffer to our main window
CopyBits(
(Bitmap*)(*offscreenPixels),
&global_window->portBits,
&global_window->portRect,
&global_window->portRect,
srcCopy,
(RgnHandle)0L,
);
// Finally, unlock the offscreen pixel buffer so the memory manager
// can move it while we aren't using it if required.
UnlockPixels(offscreenPixels);
}
}