leue@galen.crd.ge.com (Bill Leue) (10/12/89)
I've got a couple of questions regarding animation on the Mac. I know that this material has been discussed before in this group, but I didn't get all of it the first time. Please, no flames if you can restrain yourselves :-). I'm moving some fairly small (app. 100 pixels wide x 20 pixels high) sprites around using what I think is the standard method: copying the background to an offscreen bitmap, drawing the new position of the object in the offscreen bitmap, and CopyBits-ing the whole thing back to the screen. It works pretty well, and eliminates the obnoxious flickering that characterized my first, naive efforts to do animation using XOR drawing. However, there are still two glitches. First, the objects being animated are sometimes "sheared" horizontally, so that they are torn into two pieces which don't quite track each other as the object moves (say) horizontally. I'm tenatively diagnosing this problem as a lack of synchronization with the screen refresh. Am I right? If so, I suspect there's a mechanism to synchronize drawing with vertical blanking. Does anyone have any lore (or posssibly code) they feel like sharing on this subject? Second, I am using "Classic" QuickDraw to perform limited color drawing. I use ForeColor() and BackColor() calls along with a 50% grey pattern fill to get a useful range of colors using dithering. In this way I can get color objects on a color system, but don't have to make special code for monochrome systems. It works fine, except that the objects which are animated using CopyBits only display in black and white. I guess I thought that since I was using the same QD calls to write to the offscreen bitmap that I used to write the "static" portions of the screen, the objects would also show up in color. Wrong! Anyone have an idea what I'm doing wrong? Remember, I am NOT using Color QD at all -- just the 8 colors in normal QD. Thanks! -Bill Leue leue@crd.ge.com
brendan@claris.com (Brendan McCarthy) (10/13/89)
>In Article 10142 in comp.sys.mac.programmer: >leue@galen.crd.ge.com (Bill Leue) writes: >First, the objects being animated are sometimes "sheared" horizontally, so >that they are torn into two pieces which don't quite track each other as the >object moves (say)horizontally. >I'm tenatively diagnosing this problem as a lack of synchronization with the >screen refresh. Am I right? Yes, you are right. There's a cheap way to synchronize with the VBL. It works well for pretty simple drawing tasks (like CopyBits). Here's a code fragment: VAR theTicks: LONGINT; BEGIN { your screen drawing routine } ... { sync with VBL task } theTicks := TickCount; REPEAT UNTIL theTicks <> TickCount; { do your drawing now } ... END; { your drawing routine } >Second, I am using "Classic" QuickDraw to perform limited color drawing. >I use ForeColor() and BackColor() calls along with a 50% grey pattern fill >to get a useful range of colors using dithering. In this way I can >get color objects on a color system, but don't have to make special code >for monochrome systems. It works fine, except that the objects which >are animated using CopyBits only display in black and white. Right. The reason you're getting black & white is that CopyBits only moves the bits (that's its name). Classic QD doesn't store colour information anywhere. It's part of the port's drawing mode. In order to get colour from CopyBits using old QD, you'll have to SetForeColor on the CopyBits call that draws from your offscreen bitmap to the screen. I'm assuming that you currently draw the different coloured portions of your image into one offscreen bitmap, and then CopyBits that to the screen. To get the effect you want, you'll need to create an offscreen bitmap for each colour of your image, and then do the ForeColor/CopyBits thing on to the screen. Brendan : CLARIS : ........:.................................................................... : : Brendan McCarthy : Software Architect : Planning Applications : UUCP: brendan@claris.com : InterNet: {ames,apple,portal,sun,voder}!claris!brendan :
mnkonar@gorby.SRC.Honeywell.COM (Murat N. Konar) (10/13/89)
In article <10611@claris.com= brendan@claris.com (Brendan McCarthy) writes: ==In Article 10142 in comp.sys.mac.programmer: ==leue@galen.crd.ge.com (Bill Leue) writes: = ==First, the objects being animated are sometimes "sheared" horizontally, so ==that they are torn into two pieces which don't quite track each other as the ==object moves (say)horizontally. ==I'm tenatively diagnosing this problem as a lack of synchronization with the ==screen refresh. Am I right? = =Yes, you are right. There's a cheap way to synchronize with the VBL. It works =well for pretty simple drawing tasks (like CopyBits). =Here's a code fragment: = = VAR theTicks: LONGINT; = = BEGIN { your screen drawing routine } = ... = { sync with VBL task } = theTicks := TickCount; = REPEAT UNTIL theTicks <= TickCount; = { do your drawing now } = ... = END; { your drawing routine } I beleive that the above code fragment will only eliminate flicker on Mac Pluses SEs and maybe SE30s. MacII variants may have monitors that have a vertical blanking interval that is different than the normal 1/60 sec. So if you synch up with the Tick counter, you are still out of synch with the Monitor's vertical retrace. The solution appears to be to figure out which monitor you are drawing on (easy, see the chapter on GDevices in IM vol V), figure out what slot that monitor's card is in (don't know how to do that), and then synch up with that slots VBL queue (info for that puzzle is buried in IM vol V also, but not in digestible form). I too am trying to figure out how to do smooth animation on Mac IIs so if anyone out there has got the details figured out, let me know. thanks. ____________________________________________________________________ Have a day. :^| Murat N. Konar Honeywell Systems & Research Center, Camden, MN mnkonar@SRC.honeywell.com (internet) {umn-cs,ems,bthpyd}!srcsip!mnkonar(UUCP)
jackiw@cs.swarthmore.edu (Nick Jackiw) (10/13/89)
In article <3123@crdgw1.crd.ge.com> leue@galen.crd.ge.com (Bill Leue) writes: > [Discussion of offscreen animating] > However, there are still two glitches. First, the objects being animated > are sometimes "sheared" horizontally, so that they are torn into two > pieces which don't quite track each other as the object moves (say) > horizontally. I'm tenatively diagnosing this problem as a lack of > synchronization with the screen refresh. Am I right? Yes. > If so, I suspect > there's a mechanism to synchronize drawing with vertical blanking. Does > anyone have any lore (or posssibly code) they feel like sharing on this > subject? There are two cases. On the MAC 128, 128K/E, Plus, and SE, it's simple. The "tick counter" is installed as a VBL task in the screen's retrace interval. Consequently, waiting for the tickCount to change guarantees you're in the retrace interval. Code would look like this: var junk:longint; ... Delay(1,x); {Wait for next tick-count} CopyBits(...); {Start drawing as fast as you can} If you are drawing multiple objects per screen refresh, draw the ones closer to the top of the screen first. This helps keep your drawing "in front of" the electron gun. On the case of Macs with external video cards, the tick-count is not synchronized to your video redraw. Instead you'll have to make your own VBL task (which merely sets a flag visible to your application meaning "Draw now!") and install it in the VBL queue of the appropriate video card. E-mail for more details on this. > > Second, I am using "Classic" QuickDraw to perform limited color drawing. > I use ForeColor() and BackColor() calls along with a 50% grey pattern fill > to get a useful range of colors using dithering. In this way I can > get color objects on a color system, but don't have to make special code > for monochrome systems. It works fine, except that the objects which > are animated using CopyBits only display in black and white. I guess I > thought that since I was using the same QD calls to write to the offscreen > bitmap that I used to write the "static" portions of the screen, the > objects would also show up in color. Wrong! Anyone have an idea what I'm > doing wrong? Remember, I am NOT using Color QD at all -- just the 8 > colors in normal QD. You aren't doing anything wrong; you've come across a limitation of the (incredibly farseeing IMHO) original QD coloring scheme. While computers with CQD are capable of *drawing* any color they choose into your on-screen window, the bitmap in which the image is saved (i. e. yourPort^.portBits) is still only one-bit-deep (i. e. capable of expressing at most two colors). Consequently, any CopyBits(source->dest) call in which source is a 1-bit (old style) grafport can only interpret that image's data in terms of forecolor (bit=0) or backcolor (bit=1), regardless of how many different forecolors and backcolors were used in composing it. To overcome this, you'll need to go to a deeper bitmap, and get into true ColorQuickdraw. Alternately, you could create *separate* off-screen bitmaps for each "color separation" of your final object, and copyBits them (with a mask) on top of each other (changing forecolor+backcolor appropriately between calls) in your destination window. > Thanks! Hope it works. Is it, by chance, a *game?* God knows Macs could use more good animation games. > -Bill Leue > leue@crd.ge.com -- _ _|\____ Nick Jackiw | Visual Geometry Project | Math Department / /_/ O> \ ------------+-------------------------+ Swarthmore College | O> | 215-328-8225| jackiw@cs.swarthmore.edu| Swarthmore PA 19081 \_Guernica_/ ------------+-------------------------+ USA
ogden@nmsu.edu (Bill Ogden) (10/14/89)
In article <34698@srcsip.UUCP> mnkonar@gorby.SRC.Honeywell.COM (Murat N. Konar) writes: >I beleive that the above code fragment will only eliminate flicker on Mac Pluses >SEs and maybe SE30s. MacII variants may have monitors that have a vertical >blanking interval that is different than the normal 1/60 sec. So if you synch >up with the Tick counter, you are still out of synch with the Monitor's vertical >retrace. The solution appears to be to figure out which monitor you are drawing on >(easy, see the chapter on GDevices in IM vol V), figure out what slot that monitor's >card is in (don't know how to do that), and then synch up with that slots VBL queue >(info for that puzzle is buried in IM vol V also, but not in digestible form). I use GetVideoDefault to find the slot (but may NOT be the device currently being written to!) and then set up a dummy (do nothing) VBL task which decrements the vblCount every vertical blank period. It seems to work. Here's how I do it: VBLQElPtr vtrace; /* global vertical blank task pointer */ int vSlot; /* global for video slot */ InstallRetracer() /* installs the dummy vbl task in default slot */ { DefVideoRec defaultScreen; GetVideoDefault(&defaultScreen); vSlot = defaultScreen.sdSlot; vtrace =(VBLQElPtr) NewPtr(sizeof(VBLTask)); vtrace->qType = vType; vtrace->vblAddr = DummyVBLTask; vtrace->vblCount = 3000; /* count can be anything */ vtrace->vblPhase = 0; SlotVInstall(vtrace,vSlot); } waitForSync() /* call this to wait for the next vertical blank peroid */ { int wait; wait = vtrace->vblCount; while (vtrace->vblCount == wait); } Be sure and do a SlotVRemove call when you done !! This will only work on MacII's or later. Bill Ogden ogden@nmsu.edu Computing Research Lab / Psychology Dept New Mexico State University
fry@brauer.harvard.edu (D. Spotts Fry) (10/14/89)
To do synched drawing on a Mac II, you want to use a technique analagous to that of watching TickCount() on the MacPlus, but you have to make your own VBL counter. This should be done with VBLTask tied to the video slot so you really get video synching. To illustrate this, here is some code (in Think C): Dave /* my globals */ extern long VBLcounter; extern short videoSlot; extern VBLTask drawtask; /**************************************************************** Before we do anything, we have to know which slot the card is in. Use this routine. (See Start Manager in IM V.) */ FindVideoSlot() { DefVideoRec defvidrec; GetVideoDefault(&defvidrec); videoSlot = defvidrec.sdSlot; } /******************************************************************* Then we have to install a VBL Task. This is the one I'll use. To use global variables you must setup the A5 register. To do this in MultiFinder requires a special trick, which is demonstrated with the CacheA5 function. */ pascal void ScreenDrawTask() { /* * WARNING! - this is self-modifying code, but it is * necessary to run under MultiFinder...see Tech Note 180 */ long oldA5; asm { move.l a5,oldA5 move.l #0,a5 ; this will be replaced with ; CurrentA5 by the CacheA5 function at runtime } VBLcounter += 1; drawtask.vblCount = 1; asm { move.l oldA5,a5 } } pascal void CacheA5(addr) long addr; /* * This function will place CurrentA5 into the beginning of task * functions which start as: * pascal void FooTask() * { * long junk; * * asm { * move.l a5,junk * move.l #0,a5 ;this will be replaced by CurrentA5 * .... * * See Tech Note #180 */ { asm { move.l addr,a0 add.w #2,a0 ; move past JMP instruction move.l (a0),a0 add.w #0xa,a0 ; move past first part of function move.l CurrentA5,(a0) ; puts CurrentA5 in (*addr)() dc.l location } } /**************************************************************** This function actually sets up the VBL Task. */ StartCounter() { CacheA5(ScreenDrawTask); VBLcounter = 0; drawtask.vblAddr = ScreenDrawTask; drawtask.qType = vType; drawtask.vblCount = 1; drawtask.vblPhase = 0; SlotVInstall(&drawtask,videoSlot); } /********************************************************************* This function removes the VBL Task. */ RemoveCounter() { SlotVRemove(&drawtask,videoSlot); } /********************************************************************* This function holds everything until it's time to draw. Note that it could be replaced by a #define macro (make VBLjunk a global). */ WAIT_FOR_SCREEN() { long VBLjunk; VBLjunk = VBLcounter; do {} while ( VBLcounter == VBLjunk ); /* this is used to synch drawing to screen */ } /******************************************************************** Now, putting this altogether into a program */ main() { /* do your init stuff */ FindVideoSlot(); StartCounter(); /* now, whenever you want to draw something, do this... */ WAIT_FOR_SCREEN(); DrawMyThing(); /* when you're done...*/ RemoveCounter(); }