vrm@blackwater.cerc.wvu.wvnet.edu (Vasile R. Montan) (04/08/91)
Whenever anyone posts a question on comp.sys.mac.programmer about animation, the customary response is to use 'offscreen bitmaps'. While I have no problem understanding this technique in theory, I'm not sure exactly how to implement it in fact. Exactly what sort of 'offscreen' structure am I drawing into? Is it the type actually defined as BitMap? If so, how do I SetPort to draw in it, since SetPort accepts a GratPtr as its arguement? Or is it a GrafPort I am drawing into? If so, how do I tell the Toolbox that the GrafPort is off screen? I presume that CopyBits is the preferred way to put the offscreen drawing on the screen. --Kurisuto un020070@vaxa.wvnet.edu
russotto@eng.umd.edu (Matthew T. Russotto) (04/09/91)
In article <1553@babcock.cerc.wvu.wvnet.edu> un020070@vaxa.wvnet.edu writes: >Whenever anyone posts a question on comp.sys.mac.programmer about >animation, the customary response is to use 'offscreen bitmaps'. >While I have no problem understanding this technique in theory, >I'm not sure exactly how to implement it in fact. > >Exactly what sort of 'offscreen' structure am I drawing into? >Is it the type actually defined as BitMap? If so, how do I SetPort >to draw in it, since SetPort accepts a GratPtr as its arguement? >Or is it a GrafPort I am drawing into? If so, how do I tell the >Toolbox that the GrafPort is off screen? > >I presume that CopyBits is the preferred way to put the offscreen >drawing on the screen. For black and white animation, you draw into a grafport. You create a BitMap structure of the appropriate size, create a port, and call SetPortBits to associate the bitmap with the port. Copybits can be used, but if you really want to be a speed demon, you can probably optimize each transfer by aligning things right and writing tight assembly code. My guess is that you would definitely want to bypass the trap dispatcher to get speed, if you do use CopyBits (or, perhaps, BlockMove). (call NGetTrapAddress to get the address of the routine into a function pointer, and call that function) -- Matthew T. Russotto russotto@eng.umd.edu russotto@wam.umd.edu .sig under construction, like the rest of this campus.
captkidd@athena.mit.edu (Ivan Cavero Belaunde) (04/10/91)
In article <1553@babcock.cerc.wvu.wvnet.edu> un020070@vaxa.wvnet.edu writes: >Whenever anyone posts a question on comp.sys.mac.programmer about >animation, the customary response is to use 'offscreen bitmaps'. >While I have no problem understanding this technique in theory, >I'm not sure exactly how to implement it in fact. >Exactly what sort of 'offscreen' structure am I drawing into? >Is it the type actually defined as BitMap? If so, how do I SetPort >to draw in it, since SetPort accepts a GratPtr as its arguement? >Or is it a GrafPort I am drawing into? If so, how do I tell the >Toolbox that the GrafPort is off screen? The offscreen structure you draw into is either a GrafPort, a CGrafPort, or a GWorld. GrafPorts are B/W, CGrafPorts are color, and GWorlds are a mixed beast (more on this later, but note you need 32-bit QD to do GWorlds. They do make dealing with offscreen bitmaps a lot easier). You will need a BitMap or PixMap that you allocate yourself (although the OS will allocate yone for you in certain situations), and a buffer for the actual pixmap/bitmap data (which again you may have to create yourself, but not always). Basically the procedure is to create the bitmap or pixmap, set it up for drawing into it (using a port), draw into it, and CopyBits it into your destination port. If you're creating a bitmap, it's pretty simple. At the start all you need to know is the size of the image, which you use to initialize a BitMap structure. The bounds field is just a rect describing the size of the image. The rowBytes field is simply the horizontal size in pixels divided by eight and rounded up. Note that it must be even (so you might need to round up to the next word). baseAddr is just a pointer to a buffer you allocate with NewPtr (or a locked dereferenced handle), whose size is just the vertical size of your image * rowBytes. Then if the current port is a GrafPort (as opposed to a CGrafPort, which you can check by looking at the most significant bit of the rowBytes field, either thePort.portBits.rowBytes or theCPort.portVersion) you save the previous BitMap from it (thePort.portBits) and call SetPortBits. The you can draw to your heart's content in your BitMap and once you're done restore the old BitMap and do CopyBits. Minor caveat: in theory, the current port's clipRgn and visRgn could interfere with your drawing in it. I haven't had any problems, but I haven't used this technique as much as the others, so you might want to go ahead a create your own GrafPort, like you would have done if the current port was a CGrafPort, as follows: Allocate space for a GrafPort (either in the stack of via NewPtr) and call OpenPort to set it up. Then SetPortBits to the bitmap you allocated, and go ahead and draw on it. Make sure you save the old port before calling OpenPort, however. Once you're done drawing, restore the port and copy the bitmap onto the screen (or wherever). When disposing the port, call ClosePort first to get rid of the visRgn and clipRgn and then deallocate the port itself and the bitmap. Color. Color can be ugly. Color can be a bad time. But it looks a lot cooler, so you might want to do it anyway. The easiest way is to use 32bitQD and the GWorld functionality. But if you don't want to (because you don't want to require 32bitQD or you want direct access to the pixel data without having to muck around in 32-bit MMU mode), read on. With 32bit QD (yeah, it's easier): Call NewGWorld to allocate an offscreen graphics world and graphic device of whatever depth you want. Save the old GWorld, SetGWorld to the new one, lock the pixels down and draw in it. CopyBits it wherever to your heart's content and DisposeGWorld once you're done.Way nice. The only bad thing here is that if you want to have direct access to the RAM where the pixels are sitting you have to play with the MMU mode. Lock the pixels down, call GetPixBaseAddr (which returns the 32-bit mode address of the pixel data), shift into 32-bit mode (pay attention to technotes re: MMU mode swapping and the program counter) muck with the pixels, and switch back to 24-bit MMU mode. Problem here is that you shouldn't risk calling any toolbox routines at all while in 32-bit mode unless you are on a 32-bit clean machine, so you're seriously limited in what you can do. Of course, you can always check if the pixel data is in 24-bit accessible memory beforehand (the MSB of the address would be 0, I believe), and if it's not go to 32-bit MMU mode, copy it down to a buffer in main RAM, downshift to 24-bit mode, and access the pixel data there. There's all sorts of ugliness involved. I want my 32-bit clean ROMs. Ack. But I digress... Without 32bit QD: The reason this is so much more complicated is mostly because of generality. You might have a 24-bit deep screen but would like to create a 8-bit offscreen pixmap, for example, and since when creating a color port the new port takes its characteristics from the current one, there are complications associated with creating an offscreen GDevice and other ugliness. The easiest way is to save the current device using GetGDevice, get the bit depth of the deepest screen you'll be drawing into using GetGDevice and examining the PixMap, set it to be the current device, allocate a port using OpenCPort. Then modify th portPixMap's fields as required by your image (resize the bounds rect and rowBytes and allocate the buffer), as well as the port's portRect. SetPort to it and draw in it. If you want to keep your offscreen pixmap to a fixed bit depth (say 8 bits) regardless of the bit depth of the graphic devices, there's some ugliness involved because of the inverse tables and stuff. Basically instead of finding out which GDevice to use you'll have to create your own offscreen GDevice and initialize it yourself. Part of this initialization involves creating an inverse table from the CLUT you want to use (using MakeITable), so that if you CopyBits into your offscreen buffer from a different bit depth or CLUT QD will be able to adjust the colors. Once you have your offscreem GDevice you can SetGDevice to it and create the offscreen port and pixMap as above and draw in it. When initializing the device, it's useful to know that GetCTable (bitDepth) returns the system default CLUT for that bit depth. Moral: Use 32-bit QD if you're going to do weird bit depth stuff. >I presume that CopyBits is the preferred way to put the offscreen >drawing on the screen. Yup. Make sure you SetPort to the destination port (ie the screen) before copybitsing into it so that if there's varying bit depths and whatnot it can compensate by using the graphics device's inverse table. Oh, one more thing. I seem to remember that one of Apple's Mac Sample Code Packages (14?) has code on offscreen stuff. Hope this helps, -Ivan Cavero Belaunde Visualist Digital Video Applications Corp. (DiVA) Internet: captkidd@ATHENA.MIT.EDU