windows-nt/Source/XPSP1/NT/shell/osshell/ep/freecell/glide.c
2020-09-26 16:20:57 +08:00

331 lines
9.7 KiB
C

/****************************************************************************
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 <math.h> // 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]);
}