/**************************************************************************** Glide.c June 91, JimH initial code Oct 91, JimH port to Win32 Routines for gliding cards are here. There is only one public entry point to these routines, the function Glide(). The glide speed can be altered by changing STEPSIZE. A large number (like 37) makes for fast glides. ****************************************************************************/ #include "freecell.h" #include "freecons.h" #include // for labs() #define STEPSIZE 37 // size of glide steps in pixels #define BGND 255 // used for cdtDrawExt static HDC hMemB1, hMemB2, hMemF; // mem DC associated with above bitmaps static HBITMAP hOB1, hOB2, hOF; // old bitmaps in above mem DCs static UINT dwPixel[12]; // corner pixels that are saved/restored static HRGN hRgn, hRgn1, hRgn2; // hRgn1 is source, hRgn2 is destination static VOID GlideInit(HWND hWnd, UINT fcol, UINT tcol); static INT IntSqrt(INT square); static VOID SaveCorners(HDC hDC, UINT x, UINT y); static VOID RestoreCorners(HDC hDC, UINT x, UINT y); /****************************************************************************** Glide Given a from and to location, this function animates the movement of the card. ******************************************************************************/ VOID Glide(HWND hWnd, UINT fcol, UINT fpos, UINT tcol, UINT tpos) { HDC hDC; INT dx, dy; // total distance card travels UINT x1, y1, x2, y2; // start and end locations for each step UINT xStart, yStart; // beginning position UINT xEnd =0, yEnd = 0; // destination position INT i; INT distance; // distance card travles +/- 3 pixels INT steps; // number of steps card takes in glide total BOOL bSaved = FALSE; // corner pixels saved? if (fcol != tcol || fpos != tpos) // if card moves { hDC = GetDC(hWnd); hMemB1 = CreateCompatibleDC(hDC); // memory DCs for bitmaps hMemB2 = CreateCompatibleDC(hDC); hMemF = CreateCompatibleDC(hDC); hRgn1 = CreateRectRgn(1, 1, 2, 2); hRgn2 = CreateRectRgn(1, 1, 2, 2); hRgn = CreateRectRgn(1, 1, 2, 2); if (hMemB1 && hMemB2 && hMemF && hRgn1 && hRgn2 && hRgn) { hOB1 = SelectObject(hMemB1, hBM_Bgnd1); hOB2 = SelectObject(hMemB2, hBM_Bgnd2); hOF = SelectObject(hMemF, hBM_Fgnd); GlideInit(hWnd, fcol, fpos); // set up hBM_Bgnd1 and hBM_Fgnd Card2Point(fcol, fpos, &xStart, &yStart); Card2Point(tcol, tpos, &xEnd, &yEnd); SaveCorners(hDC, xEnd, yEnd); bSaved = TRUE; /* Determine how far to travel and how many steps to take. */ x1 = xStart; y1 = yStart; dx = xEnd - xStart; dy = yEnd - yStart; distance = IntSqrt(dx*dx + dy*dy); if (bFastMode) steps = 1; else steps = distance / STEPSIZE; /* Determine intermediate glide locations. Long arithmetic is needed to prevent overflows. */ for (i = 1; i < steps; i++) { x2 = xStart + ((i * dx) / steps); y2 = yStart + ((i * dy) / steps); GlideStep(hDC, x1, y1, x2, y2); x1 = x2; y1 = y2; } /* Erase last background manually -- DrawCard will do last card. */ BitBlt(hMemB1, xEnd-x1, yEnd-y1, dxCrd, dyCrd, hMemF,0,0,SRCCOPY); BitBlt(hDC, x1, y1, dxCrd, dyCrd, hMemB1, 0, 0, SRCCOPY); /* Select original bitmaps so mem DCs can be destroyed. */ SelectObject(hMemB1, hOB1); SelectObject(hMemB2, hOB2); SelectObject(hMemF, hOF); } else { LoadString(hInst, IDS_MEMORY, bigbuf, BIG); LoadString(hInst, IDS_APPNAME, smallbuf, SMALL); MessageBeep(MB_ICONHAND); MessageBox(hWnd, bigbuf, smallbuf, MB_OK | MB_ICONHAND); moveindex = 0; // don't try moving more cards PostQuitMessage(0); } DeleteDC(hMemB1); DeleteDC(hMemB2); DeleteDC(hMemF); ReleaseDC(hWnd, hDC); DeleteObject(hRgn); DeleteObject(hRgn1); DeleteObject(hRgn2); } /* Draw last card with DrawCard so end result guaranteed correct. */ hDC = GetDC(hWnd); DrawCard(hDC, tcol, tpos, card[fcol][fpos], FACEUP); if (bSaved) RestoreCorners(hDC, xEnd, yEnd); ReleaseDC(hWnd, hDC); } /****************************************************************************** GlideInit Blt what is under the card source location into hMemB1, and the card to be moved into hMemF. ******************************************************************************/ VOID GlideInit(HWND hWnd, UINT fcol, UINT fpos) { if (fcol == TOPROW) // if it's top row, background is ghost bitmap. { if (fpos > 3 && VALUE(card[fcol][fpos]) != ACE) { HDC hDC; UINT x, y; hDC = GetDC(hWnd); Card2Point(fcol, fpos, &x, &y); SaveCorners(hDC, x, y); cdtDrawExt(hMemB1,0,0,dxCrd,dyCrd,card[fcol][fpos]-4,FACEUP,BGND); RestoreCorners(hMemB1, 0, 0); ReleaseDC(hWnd, hDC); } else { SelectObject(hMemB2, hBM_Ghost); BitBlt(hMemB1, 0, 0, dxCrd, dyCrd, hMemB2, 0, 0, SRCCOPY); SelectObject(hMemB2, hBM_Bgnd2); } } else // else background contains bottom part of card above. { SelectObject(hMemB1, hBgndBrush); PatBlt(hMemB1, 0, 0, dxCrd, dyCrd, PATCOPY); if (fpos != 0) { cdtDrawExt(hMemB1, 0, 0-dyTops, dxCrd, dyCrd, card[fcol][fpos-1], FACEUP, BGND); } } /* Foreground bitmap is just the card to be moved. */ cdtDrawExt(hMemF, 0, 0, dxCrd, dyCrd, card[fcol][fpos], FACEUP, 0); } /****************************************************************************** GlideStep This routine gets called once for each step in the glide animation. On input, it needs the screen under the source in hMemB1, and the card to be moved in hMemF. It calculates the screen under the destination itself and blts it into hMemB2. At the end of the animation, it moves hMemB2 into hMemB1 so it can be call again immediately with new coordinates. ******************************************************************************/ VOID GlideStep(HDC hDC, UINT x1, UINT y1, UINT x2, UINT y2) { HDC hMemTemp; // used to swap mem DCs. SetRectRgn(hRgn1, x1, y1, x1+dxCrd, y1+dyCrd); SetRectRgn(hRgn2, x2, y2, x2+dxCrd, y2+dyCrd); /* create background of new location by combing screen background plus overlap from old background */ BitBlt(hMemB2, 0, 0, dxCrd, dyCrd, hDC, x2, y2, SRCCOPY); BitBlt(hMemB2, x1-x2, y1-y2, dxCrd, dyCrd, hMemB1, 0, 0, SRCCOPY); /* Draw old background and then draw card */ CombineRgn(hRgn, hRgn1, hRgn2, RGN_DIFF); // part of hRgn1 not in hRgn2 SelectObject(hDC, hRgn); BitBlt(hDC, x1, y1, dxCrd, dyCrd, hMemB1, 0, 0, SRCCOPY); SelectObject(hDC, hRgn2); BitBlt(hDC, x2, y2, dxCrd, dyCrd, hMemF, 0, 0, SRCCOPY); /* copy new background to old background, or rather, accomplish the same effect by swapping the associated memory device contexts. */ hMemTemp = hMemB1; hMemB1 = hMemB2; hMemB2 = hMemTemp; } /****************************************************************************** IntSqrt Newton's method to find a quick close-enough square root without pulling in the floating point libraries. f(x) == x*x - square == 0 f'(x) == 2x ******************************************************************************/ INT IntSqrt(INT square) { INT guess, lastguess; lastguess = square; guess = min(square / 2, 1024); while (abs(guess-lastguess) > 3) // 3 is close enough { lastguess = guess; guess -= ((guess * guess) - square) / (2 * guess); } return guess; } /****************************************************************************** SaveCorners RestoreCorners based on similar routines in cards.dll ******************************************************************************/ VOID SaveCorners(HDC hDC, UINT x, UINT y) { // Upper Left dwPixel[0] = GetPixel(hDC, x, y); dwPixel[1] = GetPixel(hDC, x+1, y); dwPixel[2] = GetPixel(hDC, x, y+1); // Upper Right x += dxCrd -1; dwPixel[3] = GetPixel(hDC, x, y); dwPixel[4] = GetPixel(hDC, x-1, y); dwPixel[5] = GetPixel(hDC, x, y+1); // Lower Right y += dyCrd-1; dwPixel[6] = GetPixel(hDC, x, y); dwPixel[7] = GetPixel(hDC, x, y-1); dwPixel[8] = GetPixel(hDC, x-1, y); // Lower Left x -= dxCrd-1; dwPixel[9] = GetPixel(hDC, x, y); dwPixel[10] = GetPixel(hDC, x+1, y); dwPixel[11] = GetPixel(hDC, x, y-1); } VOID RestoreCorners(HDC hDC, UINT x, UINT y) { // Upper Left SetPixel(hDC, x, y, dwPixel[0]); SetPixel(hDC, x+1, y, dwPixel[1]); SetPixel(hDC, x, y+1, dwPixel[2]); // Upper Right x += dxCrd-1; SetPixel(hDC, x, y, dwPixel[3]); SetPixel(hDC, x-1, y, dwPixel[4]); SetPixel(hDC, x, y+1, dwPixel[5]); // Lower Right y += dyCrd-1; SetPixel(hDC, x, y, dwPixel[6]); SetPixel(hDC, x, y-1, dwPixel[7]); SetPixel(hDC, x-1, y, dwPixel[8]); // Lower Left x -= dxCrd-1; SetPixel(hDC, x, y, dwPixel[9]); SetPixel(hDC, x+1, y, dwPixel[10]); SetPixel(hDC, x, y-1, dwPixel[11]); }