Leaked source code of windows server 2003
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

/**************************************************************************
*
* 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