Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!uwm.edu!psuvax1!xavier!jackiw From: jackiw@cs.swarthmore.edu (Nick Jackiw) Newsgroups: comp.sys.mac.programmer Subject: Aligning Bitmaps Summary: Brief discussion and some code Message-ID: Date: 6 Mar 90 20:28:38 GMT Sender: news@xavier.swarthmore.edu (USENET News System) Organization: Visual Geometry Project, Swarthmore College, PA Lines: 115 In Inside Mac V and in various places in the techNotes, it states that CopyBits is optimized for cases in which rowBytes is a multiple of four and in which the bitmaps are aligned. I recently did some testing on a (wide) sample of arbitrary bitmaps, and came up with the following interesting timings: o Worst case ranges from about 102-105% of average case. (Which is to say: the average case is about as bad as you can get) o Best case (perfect alignment) ranges from about 54-72% of average case. (i. e. Alignment has a *substantial* payoff.) Remember, alignment (keeping the bit-index the same between corresponding bits in the source bitmap and the destination) is only useful under several conditions: o You're always copying part of the source into one and only one corresponding part of the destination. o The source and destination are at the same bitdepth, if you're working in color. o For any given copy, the srcRect and destRect are of the same dimension (though not necessarily the same coordinates). A good example of when alignment is useful: storing an offscreen bitmap containing the image to be painted into a window. Alignment will only change (and the offscreen map be recomputed) if the window is moved. A good example of when alignment won't help at all: storing the image of a spaceship offscreen which can be copyBitted anywhere into the window. ("Anywhere"=any of 32 bit positions=alignment will only help you in 1/32 of your copies. Of course, you could store 32 differently-aligned spaceship copies offscreen, and choose the appropriate one to blit based on the dest rect.) ------- The following function might be useful for cases when you want to create an offscreen bitmap for copying to a window. Specify the window (which must already be created and in its proper location) in destWindow, and the bounds of the desired offscreen window (in the local coordinate system of destWindow). InitAlignedBitmap will generate the bitmap, tacking enough "slack" onto the left edge of the bitmap (i. e. by extending the bounds) to guarantee that any bit in theBits is aligned with the corresponding bit in the window's local coordinate system. This will guarantee you best case performance. ------ Lastly, a note or two: o If User moves the window, you'll need to recompute and redraw the bitmap. You can dispose the old one and call this function again, if you like, or you can modify this one to SetPtrSize on the existing baseAddr ptr. You may want to change the code to generate handles, instead of ptrs, but remember to lock and derefence them before drawing. Growing a window doesn't change the alignment (e. g. local [0,0] is still in the same place as it was pre-grow; therefore so is offscreen [0,0].) o Color doesn't complicate things (multiply by pixDepth before determining rowBytes, and don't forget to set the high bit), but multiple screens and multiple pixel depths do. In that you can only be in alignment with one of the multiple screens at a time (or only *guarantee* that you'll be so), you'll have to choose between aligning for the screen which is deepest (which you'll want to do if your offscreen bitmap should be complexly colored), or for the screen which has the maximum destWindow area on it (which will optimize for speed over color fidelity). Once you've figured out which GDevice to align to, plug in its pixMap for screenBits, below. Enjoy. -------- function InitAlignedBitMap(var theBits:Bitmap;destWindow:WindowPtr):boolean; {By Nick Jackiw} {Allocates storage for a bitmap the bounds of which have been set before} {calling. The bitmap will be long-word aligned with the coordinate system} {of the bit image which contains the destWindow.} var oldPort: GrafPtr; aPoint: point; bitOffset: integer; {# of bits we need to expand the offscreen map by} begin GetPort(oldPort); {Switch to desired port, for local->global conversion} SetPort(destWindow); aPoint := theBits.bounds.topLeft; LocalToGlobal(aPoint); bitOffset := (aPoint.h - ScreenBits.bounds.left) mod 32; if bitOffset < 0 then bitOffset := 32 + bitOffset; with theBits.bounds do left := left - bitOffset;{Expand dest bitmap by appropriate # of bits} with theBits do with bounds do begin rowBytes := ((right - left + 31) div 32) * 4; baseAddr := NewPtr(rowBytes * (bottom - top)); InitAlignedBitMap := baseAddr <> nil end; SetPort(oldPort) end; -- +-------------------+-jackiw@cs.swarthmore.edu / !rutgers!bpa!swatsun!jackiw-+ | nicholas jackiw | jackiw%campus.swarthmore.edu@swarthmr.bitnet | +-------------------+-VGP/MathDept/Swarthmore College, Swarthmore, PA 19081--+ "Ah...I've got this CHRONIC pain." _True Believer_