franco@bbn.com (Frank A. Lonigro) (12/08/88)
[ Go away you line eater you! ] --- Thanks again to all who responded to my previous posting with subject line "Thanks! (was "Question about GrafPort transfer modes...")" You all helped me to solve my problem. So, I'm now going to share with all of you how I solved my problem. For those of you who don't remember my previous postings, I was having a problem with transfer modes. What I wanted to do was to draw a picture, which is a game playing piece, onto a playing field(a checker board) without the rectangular shaped box around it wiping out the background. I was using DrawPicture, which was my problem. The solution was to use CopyBits. CopyBits worked fine in srcXor mode except I didn't like the result. What I wanted to achieve instead was to have a 1 or 2 pixel white border in the shape of the playing piece around the playing piece. I solved this by using ICON's instead of PICT's, by defining a mask for every icon which was 1 or 2 pixels bigger around the outer rim of the piece. Then using CopyMask to draw the ICON and MASK onto the play field. This was the most exciting programming discovery yet and I got really enthusiastic about reaching my next goal, to drag the playing piece around without wiping out the playing field background and I have you guys to thank for this one. To drag the playing piece around you need to keep track of what the background looked like before the playing piece was copied there. So moving the piece around is simply: put back the saved background; /* This makes the piece disappear * for a while until you draw it in * its new spot. This causes the * piece to "flicker" which I have a * problem with and I wish to solve. */ save the background where the piece is going to move to; copymask the icon and mask to the new spot; This method works very well, except for the "flicker". My next question: "Is their a way to eliminate this "flicker"?". I guess what I'm really asking for is a better way of restoring the background under where the piece was. Any clues????? Well anyway, for now I can deal with the "flicker". So here is sample "C" code for how to drag a playing piece around on a playing field. Actually code fragments that should get you started. /* Sample code starts here */ /* This is how I defined my playing piece. In actuality you will probably want an array of playing pieces, but this will do for an example. */ struct piece { Rect iconR, /* an offscreen rect that contains the ICON */ maskR, /* " " " the Mask */ saveR, /* what the background looked like */ destR; /* where the piece is now on the board */ int icon, /* ICON resource ID # */ mask, /* ICON Mask resource ID # */ square;/* the number of the square the piece is on */ } aPiece; /* The procedure to initialize the piece data structure */ SetUpPiece () { int itemType; Handle itemHandle; Rect itemRect; aPiece.square = boardD4; aPiece.icon = bknightICON; aPiece.mask = mknightICON; GetDItem (theDialog, aPiece.square, &itemType, &itemHandle, &itemRect); SetRect (&aPiece.destR, itemRect.left+5, itemRect.top+2, itemRect.right-5, itemRect.bottom-2); /* 32 x 32 Dest Rect */ /* should use offscreen rects */ SetRect (&aPiece.maskR, 400, 30, 432, 62); /* Mask */ SetRect (&aPiece.iconR, 432, 30, 464, 62); /* ICON */ SetRect (&aPiece.saveR, 464, 30, 496, 62); /* old */ } /* procedure to draw the piece at its current location on the playing field */ DrawPiece () { Handle theIcon, theMask; GrafPtr curPort; GetPort (&curPort); SetPort (thePlayField); /* the ICON and Mask are stored off screen */ theIcon = GetIcon (aPiece.icon); theMask = GetIcon (aPiece.mask); PlotIcon (&aPiece.maskR, theMask); PlotIcon (&aPiece.iconR, theIcon); /* Save copy of old position off screen */ CopyBits (&(thePlayField)->portBits, &(thePlayField)->portBits, &aPiece.destR, &aPiece.saveR, srcCopy, nil); /* Copy in the ICON and Mask to the playing field(destR) */ CopyMask (&(thePlayField)->portBits, /* theIcon */ &(thePlayField)->portBits, /* theMask */ &(thePlayField)->portBits, /* destination */ &aPiece.iconR, &aPiece.maskR, &aPiece.destR); SetPort (curPort); } /* So now that the playing piece is set up and drawn on the board, to drag it around you need to act accordingly to a mouseDown event. Here is how I handled this: */ case mouseDown : SetPt (&evPoint, theEvent->where.h, theEvent->where.v); where = FindWindow (evPoint, &whichWindow); switch (where) { case inContent : if (whichWindow == thePlayField) { if (PtInRect (evPoint, &aPiece.destR)) DragPiece (aPiece, evPoint); else SysBeep (1); /* Not moving a piece */ break; } /* Handle other windows */ break; } break; /* out of mouseDown */ /* HERE IS THE ROUTINE YOU ALL HAVE BEEN WAITING FOR: */ DragPiece (apiece, from) struct piece apiece; Point from; { Point current, to; GrafPtr curPort; GetPort (&curPort); SetPort (thePlayField); /* GlobalToLocal (&from); * you may need this, I didn't for some reason */ while ( StillDown() ) { /* mouse is still down */ GetMouse (¤t); if ( ! EqualPt(from, current) ) { /* mouse moved */ /* Get dh, dv */ SetPt (&to, current.h, current.v); SubPt (from, &to); /* Put back the background */ CopyBits (&(thePlayField)->portBits, &(thePlayField)->portBits, &aPiece.saveR, &aPiece.destR, srcCopy, nil); /* Move the piece */ OffsetRect (&aPiece.destR, to.h, to.v); /* Save copy of new background */ CopyBits (&(thePlayField)->portBits, &(thePlayField)->portBits, &aPiece.destR, &aPiece.saveR, srcCopy, nil); /* Copy in the ICON and Mask */ CopyMask (&(thePlayField)->portBits, &(thePlayField)->portBits, &(thePlayField)->portBits, &aPiece.iconR, &aPiece.maskR, &aPiece.destR); } /* Make from the current location */ SetPt (&from, current.h, current.v); } SetPort (curPort); } /* End of sample code */ I hope this helps out anyone who is trying to achieve playing piece animation. I look forward to your comments and any other helpful hints you can provide. I may be needing more help sooner than you think. Thanks again, -franco franco@bbn.com franco%bbn.com@relay.cs.net ...!harvard!bbn!franco PS. I don't pretend to claim that what I presented is an example of good or excelent programming practice, I only claim it to be a simple but effect learning example in achieving animation for those of you, who like me, didn't have a clue as to how to achieve it.
oster@dewey.soe.berkeley.edu (David Phillip Oster) (12/08/88)
I am the author of Shepard Tones, among many others. The reason this solution flickers is: 1.) you restore the background, erasing the old position of the piece 2.) you draw the pice in its new location. in between the two draws, there is a split second where the background is visible, because you haven't drawn the new piece yet. to get this to be smooth, you need to add the following: if the old and new positions alllow any part of the old and new objects to overlap, then do all of the above to an off-screen bitmap, then copy the result to the screen, This gets around the transient background problem. Note, this offscreen bitmap can be quite small: at most twice the size of the object you are moving.