You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1546 lines
37 KiB
1546 lines
37 KiB
/**************************************************************************
|
|
*
|
|
* Copyright (c) 1998-2000, Microsoft Corp. All Rights Reserved.
|
|
*
|
|
* Module Name:
|
|
*
|
|
* life.cpp
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Conway's game of Life using GDI+.
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 9/12/2000 asecchia - Created it.
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "life.hpp"
|
|
#include <math.h>
|
|
|
|
// This needs to be set on the command line. See the sources file.
|
|
// Turning this switch on causes the screen saver to run in a window from
|
|
// the command line, making debuggin a lot easier.
|
|
|
|
#ifdef STANDALONE_DEBUG
|
|
HINSTANCE hMainInstance;
|
|
HWND ghwndMain;
|
|
HBRUSH ghbrWhite;
|
|
|
|
TCHAR szIniFile[MAXFILELEN];
|
|
#else
|
|
extern HINSTANCE hMainInstance; /* screen saver instance handle */
|
|
#endif
|
|
|
|
// ASSERT code.
|
|
|
|
#if DBG
|
|
|
|
#define ASSERT(a) if(!(a)) { DebugBreak();}
|
|
|
|
#else
|
|
#define ASSERT(a)
|
|
#endif
|
|
|
|
// explicit unreferenced parameter.
|
|
|
|
#define UNREF(a) (a);
|
|
|
|
#define CBSIZE 40
|
|
|
|
class CachedImageArray
|
|
{
|
|
CachedBitmap *cbArray[CBSIZE];
|
|
int num;
|
|
|
|
public:
|
|
CachedImageArray()
|
|
{
|
|
num = 0;
|
|
}
|
|
|
|
// Cache an entry.
|
|
|
|
bool Add(CachedBitmap *cb)
|
|
{
|
|
if(num>=CBSIZE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
cbArray[num] = cb;
|
|
num++;
|
|
|
|
return true;
|
|
}
|
|
|
|
int Size() {return num;}
|
|
|
|
CachedBitmap *operator[] (int i)
|
|
{
|
|
if( (i<0) || (i>=num) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return cbArray[i];
|
|
}
|
|
// Throw everything away.
|
|
|
|
void Dispose()
|
|
{
|
|
for(int i=0; i<num; i++)
|
|
{
|
|
delete cbArray[i];
|
|
}
|
|
num = 0;
|
|
}
|
|
|
|
~CachedImageArray()
|
|
{
|
|
Dispose();
|
|
}
|
|
};
|
|
|
|
|
|
/**********************************************************************
|
|
*
|
|
* Handle configuration dialog
|
|
*
|
|
***********************************************************************/
|
|
|
|
|
|
INT *gLifeMatrix=NULL;
|
|
INT *gTempMatrix=NULL;
|
|
CachedImageArray *CachedImages;
|
|
INT gWidth;
|
|
INT gHeight;
|
|
|
|
DWORD gGenerationColor;
|
|
INT gSizeX;
|
|
INT gSizeY;
|
|
INT gGenerations;
|
|
INT gCurrentGeneration;
|
|
INT currentImage;
|
|
INT maxImage;
|
|
|
|
INT nTileSize;
|
|
INT nSpeed;
|
|
|
|
INT red, green, blue;
|
|
INT ri, gi, bi;
|
|
|
|
HANDLE ghFile;
|
|
|
|
WCHAR gDirPath[MAX_PATH];
|
|
|
|
|
|
|
|
|
|
struct OFFSCREENINFO
|
|
{
|
|
HDC hdc;
|
|
HBITMAP hbmpOffscreen;
|
|
HBITMAP hbmpOld;
|
|
BITMAPINFO bmi;
|
|
void *pvBits;
|
|
};
|
|
|
|
OFFSCREENINFO gOffscreenInfo = { 0 };
|
|
|
|
const int b_heptomino_x = 29;
|
|
const int b_heptomino_y = 11;
|
|
const char b_heptomino[320] =
|
|
"00000000000000000100000000000"
|
|
"11000000000000000110000000011"
|
|
"11000000000000000011000000011"
|
|
"00000000000000000110000000000"
|
|
"00000000000000000000000000000"
|
|
"00000000000000000000000000000"
|
|
"00000000000000000000000000000"
|
|
"00000000000000000110000000000"
|
|
"00000000000000000011000000000"
|
|
"00000000000000000110000000000"
|
|
"00000000000000000100000000000";
|
|
|
|
|
|
INT AsciiToUnicodeStr(
|
|
const CHAR* ansiStr,
|
|
WCHAR* unicodeStr,
|
|
INT unicodeSize
|
|
)
|
|
{
|
|
return( MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
ansiStr,
|
|
-1,
|
|
unicodeStr,
|
|
unicodeSize
|
|
) > 0 );
|
|
}
|
|
|
|
void LoadState()
|
|
{
|
|
// Retrieve the application name from the RC file.
|
|
|
|
LoadStringW(
|
|
hMainInstance,
|
|
idsAppName,
|
|
szAppName,
|
|
40
|
|
);
|
|
|
|
// Retrieve the .ini file name from the RC file.
|
|
|
|
LoadStringW(
|
|
hMainInstance,
|
|
idsIniFile,
|
|
szIniFile,
|
|
MAXFILELEN
|
|
);
|
|
|
|
// Retrieve any redraw speed data from the registry.
|
|
|
|
nSpeed = GetPrivateProfileIntW(
|
|
szAppName,
|
|
L"Redraw Speed",
|
|
SPEED_DEF,
|
|
szIniFile
|
|
);
|
|
|
|
// Only allow defined values.
|
|
|
|
nSpeed = max(nSpeed, SPEED_MIN);
|
|
nSpeed = min(nSpeed, SPEED_MAX);
|
|
|
|
|
|
// Retrieve any tile size from the registry.
|
|
|
|
nTileSize = GetPrivateProfileIntW(
|
|
szAppName,
|
|
L"Tile Size",
|
|
TILESIZE_DEF,
|
|
szIniFile
|
|
);
|
|
|
|
// Only allow defined values.
|
|
|
|
nTileSize = max(nTileSize, TILESIZE_MIN);
|
|
nTileSize = min(nTileSize, TILESIZE_MAX);
|
|
|
|
|
|
// Get the directory name. NULL if failed.
|
|
|
|
GetPrivateProfileStringW(
|
|
szAppName,
|
|
L"Image Path",
|
|
L"",
|
|
gDirPath,
|
|
MAX_PATH,
|
|
szIniFile
|
|
);
|
|
|
|
}
|
|
|
|
|
|
void SaveState()
|
|
{
|
|
WCHAR szTemp[20];
|
|
|
|
// Write out the registry setting for the speed.
|
|
|
|
wsprintf(szTemp, L"%ld", nSpeed);
|
|
|
|
WritePrivateProfileStringW(
|
|
szAppName,
|
|
L"Redraw Speed",
|
|
szTemp,
|
|
szIniFile
|
|
);
|
|
|
|
// Write out the registry setting for the tile size.
|
|
|
|
wsprintf(szTemp, L"%ld", nTileSize);
|
|
|
|
WritePrivateProfileStringW(
|
|
szAppName,
|
|
L"Tile Size",
|
|
szTemp,
|
|
szIniFile
|
|
);
|
|
|
|
// Set the directory name. NULL if failed.
|
|
|
|
WritePrivateProfileStringW(
|
|
szAppName,
|
|
L"Image Path",
|
|
gDirPath,
|
|
szIniFile
|
|
);
|
|
}
|
|
|
|
void ClearOffscreenDIB()
|
|
{
|
|
if (gOffscreenInfo.hdc)
|
|
{
|
|
PatBlt(
|
|
gOffscreenInfo.hdc,
|
|
0,
|
|
0,
|
|
gOffscreenInfo.bmi.bmiHeader.biWidth,
|
|
gOffscreenInfo.bmi.bmiHeader.biHeight,
|
|
BLACKNESS
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID CreateOffscreenDIB(HDC hdc, INT width, INT height)
|
|
{
|
|
gOffscreenInfo.bmi.bmiHeader.biSize = sizeof(gOffscreenInfo.bmi.bmiHeader);
|
|
gOffscreenInfo.bmi.bmiHeader.biWidth = width;
|
|
gOffscreenInfo.bmi.bmiHeader.biHeight = height;
|
|
gOffscreenInfo.bmi.bmiHeader.biPlanes = 1;
|
|
gOffscreenInfo.bmi.bmiHeader.biBitCount = 32;
|
|
gOffscreenInfo.bmi.bmiHeader.biCompression = BI_RGB;
|
|
|
|
gOffscreenInfo.hbmpOffscreen = CreateDIBSection(
|
|
hdc,
|
|
&gOffscreenInfo.bmi,
|
|
DIB_RGB_COLORS,
|
|
&gOffscreenInfo.pvBits,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (gOffscreenInfo.hbmpOffscreen)
|
|
{
|
|
gOffscreenInfo.hdc = CreateCompatibleDC(hdc);
|
|
|
|
if (gOffscreenInfo.hdc)
|
|
{
|
|
gOffscreenInfo.hbmpOld = (HBITMAP)SelectObject(
|
|
gOffscreenInfo.hdc,
|
|
gOffscreenInfo.hbmpOffscreen
|
|
);
|
|
|
|
ClearOffscreenDIB();
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL WINAPI ScreenSaverConfigureDialog (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static HWND hSpeed; // handle to the speed scrollbar.
|
|
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
|
|
// Load the global state.
|
|
|
|
LoadState();
|
|
|
|
// Initialize the speed scroll bar
|
|
|
|
hSpeed = GetDlgItem(hDlg, ID_SPEED);
|
|
SetScrollRange(hSpeed, SB_CTL, SPEED_MIN, SPEED_MAX, FALSE);
|
|
SetScrollPos(hSpeed, SB_CTL, nSpeed, TRUE);
|
|
|
|
// Initialize the tile size radio buttons
|
|
|
|
CheckRadioButton(hDlg, IDC_RADIOTYPE1, IDC_RADIOTYPE5, IDC_RADIOTYPE1+(TILESIZE_MAX-nTileSize));
|
|
|
|
return TRUE;
|
|
|
|
case WM_HSCROLL:
|
|
|
|
// Process the speed control scrollbar.
|
|
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case SB_PAGEUP: --nSpeed; break;
|
|
case SB_LINEUP: --nSpeed; break;
|
|
case SB_PAGEDOWN: ++nSpeed; break;
|
|
case SB_LINEDOWN: ++nSpeed; break;
|
|
case SB_THUMBPOSITION: nSpeed = HIWORD(wParam); break;
|
|
case SB_BOTTOM: nSpeed = SPEED_MIN; break;
|
|
case SB_TOP: nSpeed = SPEED_MAX; break;
|
|
case SB_THUMBTRACK:
|
|
case SB_ENDSCROLL:
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
nSpeed = max(nSpeed, SPEED_MIN);
|
|
nSpeed = min(nSpeed, SPEED_MAX);
|
|
|
|
SetScrollPos((HWND) lParam, SB_CTL, nSpeed, TRUE);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case ID_DIR:
|
|
|
|
// Do the COM thing for the SHBrowseForFolder dialog.
|
|
|
|
CoInitialize(NULL);
|
|
|
|
IMalloc *piMalloc;
|
|
if(SUCCEEDED(SHGetMalloc(&piMalloc)))
|
|
{
|
|
BROWSEINFOW bi;
|
|
memset(&bi, 0, sizeof(bi));
|
|
bi.hwndOwner = hDlg;
|
|
bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_EDITBOX;
|
|
bi.lpszTitle = L"Select image directory:";
|
|
WCHAR wszPath[MAX_PATH];
|
|
bi.pszDisplayName = wszPath;
|
|
LPITEMIDLIST lpiList = SHBrowseForFolderW(&bi);
|
|
if(lpiList)
|
|
{
|
|
if(SHGetPathFromIDListW(lpiList, wszPath))
|
|
{
|
|
wcscpy(gDirPath, wszPath);
|
|
}
|
|
piMalloc->Free(lpiList);
|
|
}
|
|
piMalloc->Release();
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
break;
|
|
|
|
case ID_OK:
|
|
|
|
// Tile size radio buttons.
|
|
|
|
if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE1))
|
|
{
|
|
nTileSize = 4;
|
|
}
|
|
else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE2))
|
|
{
|
|
nTileSize = 3;
|
|
}
|
|
else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE3))
|
|
{
|
|
nTileSize = 2;
|
|
}
|
|
else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE4))
|
|
{
|
|
nTileSize = 1;
|
|
}
|
|
else
|
|
{
|
|
nTileSize = 0; // smallest
|
|
}
|
|
|
|
SaveState();
|
|
|
|
// intentionally fall through to exit.
|
|
|
|
case ID_CANCEL:
|
|
EndDialog(hDlg, LOWORD(wParam) == IDOK);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI RegisterDialogClasses(
|
|
HANDLE hInst
|
|
)
|
|
{
|
|
return TRUE;
|
|
UNREF(hInst);
|
|
}
|
|
|
|
LRESULT WINAPI ScreenSaverProcW (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static HDC hdc; // device-context handle
|
|
static RECT rc; // RECT structure
|
|
static UINT_PTR uTimer; // timer identifier
|
|
static bool GdiplusInitialized = false;
|
|
static ULONG_PTR gpToken;
|
|
|
|
GdiplusStartupInput sti;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_CREATE:
|
|
|
|
// Initialize GDI+
|
|
|
|
if (GdiplusStartup(&gpToken, &sti, NULL) == Ok)
|
|
{
|
|
GdiplusInitialized = true;
|
|
}
|
|
|
|
// Only do work if we successfully initialized.
|
|
|
|
if(GdiplusInitialized)
|
|
{
|
|
// Retrieve the application name from the .rc file.
|
|
|
|
LoadString(hMainInstance, idsAppName, szAppName, 40);
|
|
|
|
// Initialize the global state.
|
|
|
|
GetClientRect (hwnd, &rc);
|
|
|
|
LoadState();
|
|
|
|
switch(nTileSize)
|
|
{
|
|
// 1x1 pixel
|
|
|
|
case 0:
|
|
gSizeX = 1;
|
|
gSizeY = 1;
|
|
break;
|
|
|
|
// Aspect ratio of 4x3, for pictures.
|
|
|
|
case 1:
|
|
gSizeX = 16;
|
|
gSizeY = 12;
|
|
break;
|
|
|
|
case 2:
|
|
gSizeX = 32;
|
|
gSizeY = 24;
|
|
break;
|
|
|
|
case 3:
|
|
gSizeX = 64;
|
|
gSizeY = 48;
|
|
break;
|
|
|
|
case 4:
|
|
gSizeX = 96;
|
|
gSizeY = 72;
|
|
break;
|
|
|
|
}
|
|
|
|
ghFile = 0;
|
|
gGenerations = 400;
|
|
gCurrentGeneration = 0;
|
|
|
|
gWidth = (rc.right - rc.left + 1)/gSizeX;
|
|
gHeight = (rc.bottom - rc.top + 1)/gSizeY;
|
|
|
|
gLifeMatrix = (INT *)malloc(sizeof(INT)*gWidth*gHeight);
|
|
gTempMatrix = (INT *)malloc(sizeof(INT)*gWidth*gHeight);
|
|
|
|
if(nTileSize == 0)
|
|
{
|
|
// 1x1 tilesize case.
|
|
|
|
CreateOffscreenDIB(
|
|
hdc,
|
|
rc.right - rc.left + 1,
|
|
rc.bottom - rc.top + 1
|
|
);
|
|
|
|
red = rand() % 255;
|
|
green = rand() % 255;
|
|
blue = min(255, 512 - (red + green));
|
|
|
|
ri = (rand() % 3) - 1; // 1, 0 or -1
|
|
bi = (rand() % 3) - 1; // 1, 0 or -1
|
|
gi = (rand() % 3) - 1; // 1, 0 or -1
|
|
}
|
|
else
|
|
{
|
|
// Image case.
|
|
|
|
CachedImages = new CachedImageArray();
|
|
}
|
|
|
|
maxImage = CBSIZE;
|
|
currentImage = 0; // initial number
|
|
|
|
// Set a timer for the screen saver window
|
|
|
|
uTimer = SetTimer(hwnd, 1, 1000, NULL);
|
|
|
|
srand( (unsigned)GetTickCount() );
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
|
|
// The WM_ERASEBKGND message is issued before the
|
|
// WM_TIMER message, allowing the screen saver to
|
|
// paint the background as appropriate.
|
|
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
|
|
// Only do work if we successfully initialized.
|
|
|
|
if(GdiplusInitialized)
|
|
{
|
|
if (uTimer)
|
|
{
|
|
KillTimer(hwnd, uTimer);
|
|
}
|
|
|
|
hdc = GetDC(hwnd);
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
DrawLifeIteration(hdc);
|
|
|
|
uTimer = SetTimer(hwnd, 1, nSpeed*10, NULL);
|
|
|
|
ReleaseDC(hwnd,hdc);
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
|
|
// When the WM_DESTROY message is issued, the screen saver
|
|
// must destroy any of the timers that were set at WM_CREATE
|
|
// time.
|
|
|
|
// Only do work if we successfully initialized.
|
|
|
|
if(GdiplusInitialized)
|
|
{
|
|
if (uTimer)
|
|
{
|
|
KillTimer(hwnd, uTimer);
|
|
}
|
|
|
|
free(gTempMatrix);
|
|
free(gLifeMatrix);
|
|
FindClose(ghFile);
|
|
|
|
delete CachedImages;
|
|
|
|
GdiplusShutdown(gpToken);
|
|
GdiplusInitialized = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// DefScreenSaverProc processes any messages ignored by ScreenSaverProc.
|
|
|
|
#ifdef STANDALONE_DEBUG
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
#else
|
|
return DefScreenSaverProc(hwnd, message, wParam, lParam);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
#define TEMP(x, y) gTempMatrix[ ((x+gWidth) % gWidth) + ((y+gHeight) % gHeight)*gWidth ]
|
|
#define LIFE(x, y) gLifeMatrix[ ((x+gWidth) % gWidth) + ((y+gHeight) % gHeight)*gWidth ]
|
|
|
|
|
|
inline bool AliveT(int x, int y)
|
|
{
|
|
return (TEMP(x, y) & 0x1);
|
|
}
|
|
|
|
inline bool AliveL(int x, int y)
|
|
{
|
|
return (LIFE(x, y) & 0x1);
|
|
}
|
|
|
|
inline INT CountT(int x, int y)
|
|
{
|
|
return (TEMP(x, y) >> 1);
|
|
}
|
|
|
|
inline void NewCellL(INT x, INT y)
|
|
{
|
|
ASSERT(!AliveL(x, y));
|
|
|
|
// update current cell
|
|
|
|
LIFE(x, y) += 1;
|
|
|
|
// update neighbour counts.
|
|
|
|
LIFE(x-1, y-1) += 2;
|
|
LIFE(x-1, y ) += 2;
|
|
LIFE(x-1, y+1) += 2;
|
|
LIFE(x , y-1) += 2;
|
|
LIFE(x , y+1) += 2;
|
|
LIFE(x+1, y-1) += 2;
|
|
LIFE(x+1, y ) += 2;
|
|
LIFE(x+1, y+1) += 2;
|
|
}
|
|
|
|
inline void NewCellL_NoWrap(INT index)
|
|
{
|
|
ASSERT(! (gLifeMatrix[index] & 0x1) );
|
|
|
|
// update current cell
|
|
|
|
gLifeMatrix[index] += 1;
|
|
|
|
// update neighbour counts.
|
|
|
|
gLifeMatrix[index - 1 - gWidth] += 2;
|
|
gLifeMatrix[index - 1 ] += 2;
|
|
gLifeMatrix[index - 1 + gWidth] += 2;
|
|
|
|
gLifeMatrix[index - gWidth] += 2;
|
|
|
|
gLifeMatrix[index + gWidth] += 2;
|
|
|
|
gLifeMatrix[index + 1 - gWidth] += 2;
|
|
gLifeMatrix[index + 1 ] += 2;
|
|
gLifeMatrix[index + 1 + gWidth] += 2;
|
|
}
|
|
|
|
inline void KillCellL(INT x, INT y)
|
|
{
|
|
ASSERT(AliveL(x, y));
|
|
|
|
// update current cell
|
|
|
|
LIFE(x, y) -= 1;
|
|
|
|
// update neighbour counts.
|
|
|
|
LIFE(x-1, y-1) -= 2;
|
|
LIFE(x-1, y ) -= 2;
|
|
LIFE(x-1, y+1) -= 2;
|
|
LIFE(x , y-1) -= 2;
|
|
LIFE(x , y+1) -= 2;
|
|
LIFE(x+1, y-1) -= 2;
|
|
LIFE(x+1, y ) -= 2;
|
|
LIFE(x+1, y+1) -= 2;
|
|
}
|
|
|
|
|
|
inline void KillCellL_NoWrap(INT index)
|
|
{
|
|
ASSERT(gLifeMatrix[index] & 0x1);
|
|
|
|
// update current cell
|
|
|
|
gLifeMatrix[index] -= 1;
|
|
|
|
// update neighbour counts.
|
|
|
|
gLifeMatrix[index - 1 - gWidth] -= 2;
|
|
gLifeMatrix[index - 1 ] -= 2;
|
|
gLifeMatrix[index - 1 + gWidth] -= 2;
|
|
|
|
gLifeMatrix[index - gWidth] -= 2;
|
|
|
|
gLifeMatrix[index + gWidth] -= 2;
|
|
|
|
gLifeMatrix[index + 1 - gWidth] -= 2;
|
|
gLifeMatrix[index + 1 ] -= 2;
|
|
gLifeMatrix[index + 1 + gWidth] -= 2;
|
|
}
|
|
|
|
|
|
VOID InitLifeMatrix()
|
|
{
|
|
memset(gLifeMatrix, 0, sizeof(INT)*gWidth*gHeight);
|
|
if(nTileSize == 0)
|
|
{
|
|
ClearOffscreenDIB();
|
|
}
|
|
|
|
if((rand()%2 == 0) ||
|
|
(gWidth<b_heptomino_x) ||
|
|
(gHeight<b_heptomino_y))
|
|
{
|
|
for(int i=1; i<gWidth-1; i++)
|
|
for(int j=1; j<gHeight-1; j++)
|
|
{
|
|
if((rand() % 3) == 0)
|
|
{
|
|
NewCellL_NoWrap(i + j*gWidth);
|
|
}
|
|
}
|
|
|
|
for(int i=0; i<gWidth; i++)
|
|
{
|
|
if((rand() % 3) == 0)
|
|
{
|
|
NewCellL(i, 0);
|
|
}
|
|
if((rand() % 3) == 0)
|
|
{
|
|
NewCellL(i, gHeight-1);
|
|
}
|
|
}
|
|
|
|
for(int j=1; j<gHeight-1; j++)
|
|
{
|
|
if((rand() % 3) == 0)
|
|
{
|
|
NewCellL(0, j);
|
|
}
|
|
if((rand() % 3) == 0)
|
|
{
|
|
NewCellL(gWidth-1, j);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i=0; i<b_heptomino_x; i++)
|
|
for(int j=0; j<b_heptomino_y; j++)
|
|
{
|
|
if(b_heptomino[i+j*b_heptomino_x] != '0')
|
|
{
|
|
NewCellL(i, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Bitmap *OpenBitmap()
|
|
{
|
|
Bitmap *bmp = NULL;
|
|
WIN32_FIND_DATA findData = {0};
|
|
WCHAR filename[1024];
|
|
|
|
do
|
|
{
|
|
// don't leak if we repeat this loop due to an invalid bitmap.
|
|
|
|
delete bmp; bmp = NULL;
|
|
|
|
if(ghFile)
|
|
{
|
|
if(!FindNextFileW(ghFile, &findData))
|
|
{
|
|
// finished going through the list.
|
|
|
|
FindClose(ghFile);
|
|
ghFile = 0;
|
|
maxImage = currentImage;
|
|
currentImage = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if(!ghFile)
|
|
{
|
|
currentImage = 0; // we're about to increment this.
|
|
|
|
wsprintf(filename, L"%s\\*.*", gDirPath);
|
|
ghFile = FindFirstFileW(filename, &findData);
|
|
|
|
if(!ghFile)
|
|
{
|
|
return NULL; // No files.
|
|
}
|
|
}
|
|
|
|
if((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
|
{
|
|
wsprintf(
|
|
filename,
|
|
L"%s\\%s",
|
|
gDirPath,
|
|
findData.cFileName
|
|
);
|
|
|
|
bmp = new Bitmap(filename);
|
|
}
|
|
|
|
// !!! need to prevent infinite loops.
|
|
} while (
|
|
((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
|
|
FILE_ATTRIBUTE_DIRECTORY) ||
|
|
(bmp->GetLastStatus() != Ok)
|
|
);
|
|
|
|
return bmp;
|
|
}
|
|
|
|
VOID InitialPaintPicture(Graphics *g, CachedBitmap *cb)
|
|
{
|
|
SolidBrush OffBrush(Color(0xff000000));
|
|
|
|
for(int x=0; x<gWidth; x++)
|
|
{
|
|
for(int y=0; y<gHeight; y++)
|
|
{
|
|
if(AliveL(x, y))
|
|
{
|
|
g->DrawCachedBitmap(
|
|
cb,
|
|
x*gSizeX,
|
|
y*gSizeY
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// we should really use a bitblt for this.
|
|
|
|
g->FillRectangle(
|
|
&OffBrush,
|
|
x*gSizeX,
|
|
y*gSizeY,
|
|
gSizeX,
|
|
gSizeY
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID InitialPaintPixel()
|
|
{
|
|
ASSERT(nTileSize == 0);
|
|
|
|
DWORD *pixel = (DWORD*)(gOffscreenInfo.pvBits);
|
|
|
|
ASSERT(pixel != NULL);
|
|
|
|
for(int x=0; x<gWidth; x++)
|
|
{
|
|
for(int y=0; y<gHeight; y++)
|
|
{
|
|
// we know there's no wrapping, we shouldn't have to do the mod.
|
|
INT index = x+y*gWidth;
|
|
|
|
if(gLifeMatrix[index] & 0x1)
|
|
{
|
|
pixel[index] = gGenerationColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
CachedBitmap *MakeCachedBitmapEntry(Bitmap *bmp, Graphics *gfxMain)
|
|
{
|
|
// Make a tile bitmap and wrap a graphics around it.
|
|
|
|
Bitmap *tileBmp = new Bitmap(gSizeX, gSizeY, PixelFormat32bppPARGB);
|
|
Graphics *g = new Graphics(tileBmp);
|
|
|
|
// Shrink the image down to the tile size using the Bicubic filter.
|
|
|
|
g->SetInterpolationMode(InterpolationModeHighQualityBicubic);
|
|
|
|
g->DrawImage(
|
|
bmp,
|
|
Rect(0,0,gSizeX,gSizeY),
|
|
0,
|
|
0,
|
|
bmp->GetWidth(),
|
|
bmp->GetHeight(),
|
|
UnitPixel
|
|
);
|
|
|
|
// Create the CachedBitmap from the tile.
|
|
|
|
CachedBitmap *cb = new CachedBitmap(tileBmp, gfxMain);
|
|
|
|
// clean up.
|
|
|
|
delete g;
|
|
delete tileBmp;
|
|
|
|
return cb;
|
|
}
|
|
|
|
VOID IteratePixelGeneration()
|
|
{
|
|
DWORD *pixel = (DWORD*)(gOffscreenInfo.pvBits);
|
|
INT count;
|
|
INT index = 1+gWidth;
|
|
|
|
for(int y=1; y<gHeight-1; y++)
|
|
{
|
|
for(int x=1; x<gWidth-1; x++)
|
|
{
|
|
if(gTempMatrix[index] != 0)
|
|
{
|
|
// If the cell is not alive and it's neighbour count
|
|
// is exactly 3, it is born.
|
|
|
|
if( gTempMatrix[index] == 6 )
|
|
{
|
|
// A new cell is born into an empty square.
|
|
|
|
NewCellL_NoWrap(index);
|
|
pixel[index] = gGenerationColor;
|
|
}
|
|
else
|
|
{
|
|
count = gTempMatrix[index] >> 1;
|
|
|
|
// if the cell is alive and its neighbour count
|
|
// is not 2 or 3, it dies.
|
|
|
|
if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
|
|
{
|
|
// Kill the cell - overcrowding or not enough support.
|
|
|
|
KillCellL_NoWrap(index);
|
|
pixel[index] = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
index++;
|
|
}
|
|
|
|
// skip the wrap boundaries.
|
|
index += 2;
|
|
}
|
|
|
|
index = 0;
|
|
|
|
for(int y=0; y<gHeight; y++)
|
|
{
|
|
// left vertical edge.
|
|
|
|
if(gTempMatrix[index] != 0)
|
|
{
|
|
// If the cell is not alive and it's neighbour count
|
|
// is exactly 3, it is born.
|
|
|
|
if( gTempMatrix[index] == 6 )
|
|
{
|
|
// A new cell is born into an empty square.
|
|
|
|
NewCellL(0, y);
|
|
pixel[index] = gGenerationColor;
|
|
}
|
|
else
|
|
{
|
|
count = gTempMatrix[index] >> 1;
|
|
|
|
// if the cell is alive and its neighbour count
|
|
// is not 2 or 3, it dies.
|
|
|
|
if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
|
|
{
|
|
// Kill the cell - overcrowding or not enough support.
|
|
|
|
KillCellL(0, y);
|
|
pixel[index] = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// right vertical edge
|
|
|
|
index += gWidth-1;
|
|
|
|
if(gTempMatrix[index] != 0)
|
|
{
|
|
// If the cell is not alive and it's neighbour count
|
|
// is exactly 3, it is born.
|
|
|
|
if( gTempMatrix[index] == 6 )
|
|
{
|
|
// A new cell is born into an empty square.
|
|
|
|
NewCellL(gWidth-1, y);
|
|
pixel[index] = gGenerationColor;
|
|
}
|
|
else
|
|
{
|
|
count = gTempMatrix[index] >> 1;
|
|
|
|
// if the cell is alive and its neighbour count
|
|
// is not 2 or 3, it dies.
|
|
|
|
if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
|
|
{
|
|
// Kill the cell - overcrowding or not enough support.
|
|
|
|
KillCellL(gWidth-1, y);
|
|
pixel[index] = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// next scanline.
|
|
|
|
index++;
|
|
}
|
|
|
|
|
|
index = 1;
|
|
INT index2 = index + (gHeight-1)*gWidth;
|
|
|
|
for(int x=1; x<gWidth-1; x++)
|
|
{
|
|
// top edge.
|
|
|
|
if(gTempMatrix[index] != 0)
|
|
{
|
|
// If the cell is not alive and it's neighbour count
|
|
// is exactly 3, it is born.
|
|
|
|
if( gTempMatrix[index] == 6 )
|
|
{
|
|
// A new cell is born into an empty square.
|
|
|
|
NewCellL(x, 0);
|
|
pixel[index] = gGenerationColor;
|
|
}
|
|
else
|
|
{
|
|
count = gTempMatrix[index] >> 1;
|
|
|
|
// if the cell is alive and its neighbour count
|
|
// is not 2 or 3, it dies.
|
|
|
|
if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
|
|
{
|
|
// Kill the cell - overcrowding or not enough support.
|
|
|
|
KillCellL(x, 0);
|
|
pixel[index] = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
index++;
|
|
|
|
// bottom edge
|
|
|
|
if(gTempMatrix[index2] != 0)
|
|
{
|
|
// If the cell is not alive and it's neighbour count
|
|
// is exactly 3, it is born.
|
|
|
|
if( gTempMatrix[index2] == 6 )
|
|
{
|
|
// A new cell is born into an empty square.
|
|
|
|
NewCellL(x, gHeight-1);
|
|
pixel[index2] = gGenerationColor;
|
|
}
|
|
else
|
|
{
|
|
count = gTempMatrix[index2] >> 1;
|
|
|
|
// if the cell is alive and its neighbour count
|
|
// is not 2 or 3, it dies.
|
|
|
|
if( (gTempMatrix[index2] & 0x1) && ((count<2) || (count>3)) )
|
|
{
|
|
// Kill the cell - overcrowding or not enough support.
|
|
|
|
KillCellL(x, gHeight-1);
|
|
pixel[index2] = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// next pixel.
|
|
|
|
index2++;
|
|
}
|
|
}
|
|
|
|
VOID IteratePictureGeneration(Graphics &g, CachedBitmap *cb)
|
|
{
|
|
SolidBrush OffBrush(Color(0xff000000));
|
|
|
|
INT count;
|
|
INT *cell = gTempMatrix;
|
|
|
|
for(int y=0; y<gHeight; y++)
|
|
{
|
|
for(int x=0; x<gWidth; x++)
|
|
{
|
|
if(*cell != 0)
|
|
{
|
|
// If the cell is not alive and it's neighbour count
|
|
// is exactly 3, it is born.
|
|
|
|
if( *cell == 6 )
|
|
{
|
|
// A new cell is born into an empty square.
|
|
|
|
NewCellL(x, y);
|
|
|
|
g.DrawCachedBitmap(
|
|
cb,
|
|
x*gSizeX,
|
|
y*gSizeY
|
|
);
|
|
}
|
|
else
|
|
{
|
|
count = *cell >> 1;
|
|
|
|
// if the cell is alive and its neighbour count
|
|
// is not 2 or 3, it dies.
|
|
|
|
if( (*cell & 0x1) && ((count<2) || (count>3)) )
|
|
{
|
|
// Kill the cell - overcrowding or not enough support.
|
|
|
|
KillCellL(x, y);
|
|
|
|
g.FillRectangle(
|
|
&OffBrush,
|
|
x*gSizeX,
|
|
y*gSizeY,
|
|
gSizeX,
|
|
gSizeY
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|
|
cell++;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID RandomizeColor()
|
|
{
|
|
if(rand() % 200 == 0)
|
|
{
|
|
ri = (rand() % 3) - 1; // 1, 0 or -1
|
|
}
|
|
if(rand() % 200 == 0)
|
|
{
|
|
gi = (rand() % 3) - 1; // 1, 0 or -1
|
|
}
|
|
if(rand() % 200 == 0)
|
|
{
|
|
bi = (rand() % 3) - 1; // 1, 0 or -1
|
|
}
|
|
|
|
if((red < 100) && (green < 100) && (blue < 100))
|
|
{
|
|
if(red > green && red > blue)
|
|
{
|
|
ri = 1;
|
|
}
|
|
else if (green > blue)
|
|
{
|
|
gi = 1;
|
|
}
|
|
else
|
|
{
|
|
bi = 1;
|
|
}
|
|
}
|
|
// bounce off the extrema.
|
|
|
|
if(red == 0)
|
|
{
|
|
ri = 1;
|
|
}
|
|
|
|
if(red == 255)
|
|
{
|
|
ri = -1;
|
|
}
|
|
|
|
if(green == 0)
|
|
{
|
|
gi = 1;
|
|
}
|
|
|
|
if(green == 255)
|
|
{
|
|
gi = -1;
|
|
}
|
|
|
|
if(blue == 0)
|
|
{
|
|
bi = 1;
|
|
}
|
|
|
|
if(blue == 255)
|
|
{
|
|
bi = -1;
|
|
}
|
|
|
|
red += ri;
|
|
green += gi;
|
|
blue += bi;
|
|
}
|
|
|
|
|
|
VOID DrawLifeIteration(HDC hdc)
|
|
{
|
|
// Are we initialized yet?
|
|
|
|
if(!gLifeMatrix || !gTempMatrix) { return; }
|
|
|
|
Graphics g(hdc);
|
|
|
|
g.SetSmoothingMode(SmoothingModeNone);
|
|
|
|
Bitmap *bmp = NULL;
|
|
CachedBitmap *cb = NULL;
|
|
|
|
// currentImage should never be larger than CBSIZE at this point.
|
|
|
|
ASSERT(currentImage < CBSIZE);
|
|
|
|
if(nTileSize==0)
|
|
{
|
|
// cycle color.
|
|
|
|
RandomizeColor();
|
|
gGenerationColor = RGB(red, green, blue);
|
|
}
|
|
else
|
|
{
|
|
// Fetch bitmaps from the image directory.
|
|
|
|
if(currentImage >= CachedImages->Size()) {
|
|
|
|
// We haven't filled up the cache yet. Keep opening images.
|
|
|
|
bmp = OpenBitmap();
|
|
}
|
|
|
|
// Did we get a new bitmap?
|
|
|
|
if(bmp)
|
|
{
|
|
cb = MakeCachedBitmapEntry(bmp, &g);
|
|
|
|
if(cb)
|
|
{
|
|
// Put it in the cache.
|
|
|
|
CachedImages->Add(cb);
|
|
currentImage++;
|
|
}
|
|
|
|
delete bmp; bmp = NULL;
|
|
}
|
|
else
|
|
{
|
|
cb = (*CachedImages)[currentImage];
|
|
currentImage++;
|
|
}
|
|
|
|
if( (currentImage >= CBSIZE) ||
|
|
(currentImage >= maxImage) )
|
|
{
|
|
currentImage = 0;
|
|
}
|
|
|
|
if(!cb)
|
|
{
|
|
// we failed to get an image tile.
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// update the generation and see if we need to do the first generation.
|
|
|
|
//gCurrentGeneration--;
|
|
if(gCurrentGeneration <= 0)
|
|
{
|
|
// gCurrentGeneration = gGenerations;
|
|
gCurrentGeneration++;
|
|
InitLifeMatrix();
|
|
if(nTileSize == 0)
|
|
{
|
|
InitialPaintPixel();
|
|
}
|
|
else
|
|
{
|
|
InitialPaintPicture(&g, cb);
|
|
}
|
|
goto Done;
|
|
}
|
|
gCurrentGeneration++;
|
|
|
|
|
|
// Make a copy of the life matrix.
|
|
|
|
memcpy(gTempMatrix, gLifeMatrix, sizeof(INT)*gWidth*gHeight);
|
|
|
|
if(nTileSize==0)
|
|
{
|
|
IteratePixelGeneration();
|
|
|
|
ASSERT(gSizeX == 1);
|
|
ASSERT(gSizeY == 1);
|
|
|
|
StretchBlt(
|
|
hdc,
|
|
0,
|
|
0,
|
|
gWidth,
|
|
gHeight,
|
|
gOffscreenInfo.hdc,
|
|
0,
|
|
0,
|
|
gWidth,
|
|
gHeight,
|
|
SRCCOPY
|
|
);
|
|
}
|
|
else
|
|
{
|
|
IteratePictureGeneration(g, cb);
|
|
}
|
|
|
|
// 5% mutation.
|
|
/*
|
|
if(((float)(rand())/RAND_MAX) < 0.05f)
|
|
{
|
|
int x = rand()*gWidth/RAND_MAX;
|
|
int y = rand()*gHeight/RAND_MAX;
|
|
|
|
if(AliveL(x, y))
|
|
{
|
|
KillCellL(x, y);
|
|
|
|
g.FillRectangle(
|
|
&OffBrush,
|
|
x*gSizeX,
|
|
y*gSizeY,
|
|
gSizeX,
|
|
gSizeY
|
|
);
|
|
}
|
|
else
|
|
{
|
|
NewCellL(x, y);
|
|
|
|
g.DrawCachedBitmap(
|
|
cb,
|
|
x*gSizeX,
|
|
y*gSizeY
|
|
);
|
|
}
|
|
}
|
|
*/
|
|
Done:
|
|
;
|
|
}
|
|
|
|
|
|
#ifdef STANDALONE_DEBUG
|
|
|
|
LONG_PTR
|
|
lMainWindowProc(
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
switch(message)
|
|
{
|
|
// Handle the destroy message.
|
|
|
|
case WM_DESTROY:
|
|
DeleteObject(ghbrWhite);
|
|
PostQuitMessage(0);
|
|
break;
|
|
}
|
|
|
|
// Hook into the screen saver windproc.
|
|
|
|
return(ScreenSaverProcW(hwnd, message, wParam, lParam));
|
|
}
|
|
|
|
BOOL bInitApp(VOID)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
// not quite so white background brush.
|
|
ghbrWhite = CreateSolidBrush(RGB(0xFF,0xFF,0xFF));
|
|
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = lMainWindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hMainInstance;
|
|
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = ghbrWhite;
|
|
wc.lpszMenuName = L"MainMenu";
|
|
wc.lpszClassName = L"TestClass";
|
|
|
|
if(!RegisterClass(&wc)) { return FALSE; }
|
|
|
|
ghwndMain = CreateWindowExW(
|
|
0,
|
|
L"TestClass",
|
|
L"Win32 Test",
|
|
WS_OVERLAPPED |
|
|
WS_CAPTION |
|
|
WS_BORDER |
|
|
WS_THICKFRAME |
|
|
WS_MAXIMIZEBOX |
|
|
WS_MINIMIZEBOX |
|
|
WS_CLIPCHILDREN |
|
|
WS_VISIBLE |
|
|
WS_SYSMENU,
|
|
80,
|
|
70,
|
|
800,
|
|
600,
|
|
NULL,
|
|
NULL,
|
|
hMainInstance,
|
|
NULL
|
|
);
|
|
|
|
if (ghwndMain == NULL)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
SetFocus(ghwndMain);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
void _cdecl main(
|
|
INT argc,
|
|
PCHAR argv[]
|
|
)
|
|
{
|
|
MSG msg;
|
|
|
|
hMainInstance = GetModuleHandle(NULL);
|
|
|
|
if(!bInitApp()) {return;}
|
|
|
|
while(GetMessage (&msg, NULL, 0, 0))
|
|
{
|
|
if((ghwndMain == 0) || !IsDialogMessage(ghwndMain, &msg)) {
|
|
TranslateMessage(&msg) ;
|
|
DispatchMessage(&msg) ;
|
|
}
|
|
}
|
|
|
|
return;
|
|
UNREF(argc);
|
|
UNREF(argv);
|
|
}
|
|
|
|
#endif
|