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

  1. /**************************************************************************
  2. *
  3. * Copyright (c) 1998-2000, Microsoft Corp. All Rights Reserved.
  4. *
  5. * Module Name:
  6. *
  7. * life.cpp
  8. *
  9. * Abstract:
  10. *
  11. * Conway's game of Life using GDI+.
  12. *
  13. * Revision History:
  14. *
  15. * 9/12/2000 asecchia - Created it.
  16. *
  17. ***************************************************************************/
  18. #include "life.hpp"
  19. #include <math.h>
  20. // This needs to be set on the command line. See the sources file.
  21. // Turning this switch on causes the screen saver to run in a window from
  22. // the command line, making debuggin a lot easier.
  23. #ifdef STANDALONE_DEBUG
  24. HINSTANCE hMainInstance;
  25. HWND ghwndMain;
  26. HBRUSH ghbrWhite;
  27. TCHAR szIniFile[MAXFILELEN];
  28. #else
  29. extern HINSTANCE hMainInstance; /* screen saver instance handle */
  30. #endif
  31. // ASSERT code.
  32. #if DBG
  33. #define ASSERT(a) if(!(a)) { DebugBreak();}
  34. #else
  35. #define ASSERT(a)
  36. #endif
  37. // explicit unreferenced parameter.
  38. #define UNREF(a) (a);
  39. #define CBSIZE 40
  40. class CachedImageArray
  41. {
  42. CachedBitmap *cbArray[CBSIZE];
  43. int num;
  44. public:
  45. CachedImageArray()
  46. {
  47. num = 0;
  48. }
  49. // Cache an entry.
  50. bool Add(CachedBitmap *cb)
  51. {
  52. if(num>=CBSIZE)
  53. {
  54. return false;
  55. }
  56. cbArray[num] = cb;
  57. num++;
  58. return true;
  59. }
  60. int Size() {return num;}
  61. CachedBitmap *operator[] (int i)
  62. {
  63. if( (i<0) || (i>=num) )
  64. {
  65. return NULL;
  66. }
  67. return cbArray[i];
  68. }
  69. // Throw everything away.
  70. void Dispose()
  71. {
  72. for(int i=0; i<num; i++)
  73. {
  74. delete cbArray[i];
  75. }
  76. num = 0;
  77. }
  78. ~CachedImageArray()
  79. {
  80. Dispose();
  81. }
  82. };
  83. /**********************************************************************
  84. *
  85. * Handle configuration dialog
  86. *
  87. ***********************************************************************/
  88. INT *gLifeMatrix=NULL;
  89. INT *gTempMatrix=NULL;
  90. CachedImageArray *CachedImages;
  91. INT gWidth;
  92. INT gHeight;
  93. DWORD gGenerationColor;
  94. INT gSizeX;
  95. INT gSizeY;
  96. INT gGenerations;
  97. INT gCurrentGeneration;
  98. INT currentImage;
  99. INT maxImage;
  100. INT nTileSize;
  101. INT nSpeed;
  102. INT red, green, blue;
  103. INT ri, gi, bi;
  104. HANDLE ghFile;
  105. WCHAR gDirPath[MAX_PATH];
  106. struct OFFSCREENINFO
  107. {
  108. HDC hdc;
  109. HBITMAP hbmpOffscreen;
  110. HBITMAP hbmpOld;
  111. BITMAPINFO bmi;
  112. void *pvBits;
  113. };
  114. OFFSCREENINFO gOffscreenInfo = { 0 };
  115. const int b_heptomino_x = 29;
  116. const int b_heptomino_y = 11;
  117. const char b_heptomino[320] =
  118. "00000000000000000100000000000"
  119. "11000000000000000110000000011"
  120. "11000000000000000011000000011"
  121. "00000000000000000110000000000"
  122. "00000000000000000000000000000"
  123. "00000000000000000000000000000"
  124. "00000000000000000000000000000"
  125. "00000000000000000110000000000"
  126. "00000000000000000011000000000"
  127. "00000000000000000110000000000"
  128. "00000000000000000100000000000";
  129. INT AsciiToUnicodeStr(
  130. const CHAR* ansiStr,
  131. WCHAR* unicodeStr,
  132. INT unicodeSize
  133. )
  134. {
  135. return( MultiByteToWideChar(
  136. CP_ACP,
  137. 0,
  138. ansiStr,
  139. -1,
  140. unicodeStr,
  141. unicodeSize
  142. ) > 0 );
  143. }
  144. void LoadState()
  145. {
  146. // Retrieve the application name from the RC file.
  147. LoadStringW(
  148. hMainInstance,
  149. idsAppName,
  150. szAppName,
  151. 40
  152. );
  153. // Retrieve the .ini file name from the RC file.
  154. LoadStringW(
  155. hMainInstance,
  156. idsIniFile,
  157. szIniFile,
  158. MAXFILELEN
  159. );
  160. // Retrieve any redraw speed data from the registry.
  161. nSpeed = GetPrivateProfileIntW(
  162. szAppName,
  163. L"Redraw Speed",
  164. SPEED_DEF,
  165. szIniFile
  166. );
  167. // Only allow defined values.
  168. nSpeed = max(nSpeed, SPEED_MIN);
  169. nSpeed = min(nSpeed, SPEED_MAX);
  170. // Retrieve any tile size from the registry.
  171. nTileSize = GetPrivateProfileIntW(
  172. szAppName,
  173. L"Tile Size",
  174. TILESIZE_DEF,
  175. szIniFile
  176. );
  177. // Only allow defined values.
  178. nTileSize = max(nTileSize, TILESIZE_MIN);
  179. nTileSize = min(nTileSize, TILESIZE_MAX);
  180. // Get the directory name. NULL if failed.
  181. GetPrivateProfileStringW(
  182. szAppName,
  183. L"Image Path",
  184. L"",
  185. gDirPath,
  186. MAX_PATH,
  187. szIniFile
  188. );
  189. }
  190. void SaveState()
  191. {
  192. WCHAR szTemp[20];
  193. // Write out the registry setting for the speed.
  194. wsprintf(szTemp, L"%ld", nSpeed);
  195. WritePrivateProfileStringW(
  196. szAppName,
  197. L"Redraw Speed",
  198. szTemp,
  199. szIniFile
  200. );
  201. // Write out the registry setting for the tile size.
  202. wsprintf(szTemp, L"%ld", nTileSize);
  203. WritePrivateProfileStringW(
  204. szAppName,
  205. L"Tile Size",
  206. szTemp,
  207. szIniFile
  208. );
  209. // Set the directory name. NULL if failed.
  210. WritePrivateProfileStringW(
  211. szAppName,
  212. L"Image Path",
  213. gDirPath,
  214. szIniFile
  215. );
  216. }
  217. void ClearOffscreenDIB()
  218. {
  219. if (gOffscreenInfo.hdc)
  220. {
  221. PatBlt(
  222. gOffscreenInfo.hdc,
  223. 0,
  224. 0,
  225. gOffscreenInfo.bmi.bmiHeader.biWidth,
  226. gOffscreenInfo.bmi.bmiHeader.biHeight,
  227. BLACKNESS
  228. );
  229. }
  230. }
  231. VOID CreateOffscreenDIB(HDC hdc, INT width, INT height)
  232. {
  233. gOffscreenInfo.bmi.bmiHeader.biSize = sizeof(gOffscreenInfo.bmi.bmiHeader);
  234. gOffscreenInfo.bmi.bmiHeader.biWidth = width;
  235. gOffscreenInfo.bmi.bmiHeader.biHeight = height;
  236. gOffscreenInfo.bmi.bmiHeader.biPlanes = 1;
  237. gOffscreenInfo.bmi.bmiHeader.biBitCount = 32;
  238. gOffscreenInfo.bmi.bmiHeader.biCompression = BI_RGB;
  239. gOffscreenInfo.hbmpOffscreen = CreateDIBSection(
  240. hdc,
  241. &gOffscreenInfo.bmi,
  242. DIB_RGB_COLORS,
  243. &gOffscreenInfo.pvBits,
  244. NULL,
  245. 0
  246. );
  247. if (gOffscreenInfo.hbmpOffscreen)
  248. {
  249. gOffscreenInfo.hdc = CreateCompatibleDC(hdc);
  250. if (gOffscreenInfo.hdc)
  251. {
  252. gOffscreenInfo.hbmpOld = (HBITMAP)SelectObject(
  253. gOffscreenInfo.hdc,
  254. gOffscreenInfo.hbmpOffscreen
  255. );
  256. ClearOffscreenDIB();
  257. }
  258. }
  259. }
  260. BOOL WINAPI ScreenSaverConfigureDialog (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  261. {
  262. static HWND hSpeed; // handle to the speed scrollbar.
  263. switch(message)
  264. {
  265. case WM_INITDIALOG:
  266. // Load the global state.
  267. LoadState();
  268. // Initialize the speed scroll bar
  269. hSpeed = GetDlgItem(hDlg, ID_SPEED);
  270. SetScrollRange(hSpeed, SB_CTL, SPEED_MIN, SPEED_MAX, FALSE);
  271. SetScrollPos(hSpeed, SB_CTL, nSpeed, TRUE);
  272. // Initialize the tile size radio buttons
  273. CheckRadioButton(hDlg, IDC_RADIOTYPE1, IDC_RADIOTYPE5, IDC_RADIOTYPE1+(TILESIZE_MAX-nTileSize));
  274. return TRUE;
  275. case WM_HSCROLL:
  276. // Process the speed control scrollbar.
  277. switch (LOWORD(wParam))
  278. {
  279. case SB_PAGEUP: --nSpeed; break;
  280. case SB_LINEUP: --nSpeed; break;
  281. case SB_PAGEDOWN: ++nSpeed; break;
  282. case SB_LINEDOWN: ++nSpeed; break;
  283. case SB_THUMBPOSITION: nSpeed = HIWORD(wParam); break;
  284. case SB_BOTTOM: nSpeed = SPEED_MIN; break;
  285. case SB_TOP: nSpeed = SPEED_MAX; break;
  286. case SB_THUMBTRACK:
  287. case SB_ENDSCROLL:
  288. return TRUE;
  289. break;
  290. }
  291. nSpeed = max(nSpeed, SPEED_MIN);
  292. nSpeed = min(nSpeed, SPEED_MAX);
  293. SetScrollPos((HWND) lParam, SB_CTL, nSpeed, TRUE);
  294. break;
  295. case WM_COMMAND:
  296. switch(LOWORD(wParam))
  297. {
  298. case ID_DIR:
  299. // Do the COM thing for the SHBrowseForFolder dialog.
  300. CoInitialize(NULL);
  301. IMalloc *piMalloc;
  302. if(SUCCEEDED(SHGetMalloc(&piMalloc)))
  303. {
  304. BROWSEINFOW bi;
  305. memset(&bi, 0, sizeof(bi));
  306. bi.hwndOwner = hDlg;
  307. bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_EDITBOX;
  308. bi.lpszTitle = L"Select image directory:";
  309. WCHAR wszPath[MAX_PATH];
  310. bi.pszDisplayName = wszPath;
  311. LPITEMIDLIST lpiList = SHBrowseForFolderW(&bi);
  312. if(lpiList)
  313. {
  314. if(SHGetPathFromIDListW(lpiList, wszPath))
  315. {
  316. wcscpy(gDirPath, wszPath);
  317. }
  318. piMalloc->Free(lpiList);
  319. }
  320. piMalloc->Release();
  321. }
  322. CoUninitialize();
  323. break;
  324. case ID_OK:
  325. // Tile size radio buttons.
  326. if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE1))
  327. {
  328. nTileSize = 4;
  329. }
  330. else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE2))
  331. {
  332. nTileSize = 3;
  333. }
  334. else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE3))
  335. {
  336. nTileSize = 2;
  337. }
  338. else if (IsDlgButtonChecked(hDlg, IDC_RADIOTYPE4))
  339. {
  340. nTileSize = 1;
  341. }
  342. else
  343. {
  344. nTileSize = 0; // smallest
  345. }
  346. SaveState();
  347. // intentionally fall through to exit.
  348. case ID_CANCEL:
  349. EndDialog(hDlg, LOWORD(wParam) == IDOK);
  350. return TRUE;
  351. }
  352. }
  353. return FALSE;
  354. }
  355. BOOL WINAPI RegisterDialogClasses(
  356. HANDLE hInst
  357. )
  358. {
  359. return TRUE;
  360. UNREF(hInst);
  361. }
  362. LRESULT WINAPI ScreenSaverProcW (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  363. {
  364. static HDC hdc; // device-context handle
  365. static RECT rc; // RECT structure
  366. static UINT_PTR uTimer; // timer identifier
  367. static bool GdiplusInitialized = false;
  368. static ULONG_PTR gpToken;
  369. GdiplusStartupInput sti;
  370. switch(message)
  371. {
  372. case WM_CREATE:
  373. // Initialize GDI+
  374. if (GdiplusStartup(&gpToken, &sti, NULL) == Ok)
  375. {
  376. GdiplusInitialized = true;
  377. }
  378. // Only do work if we successfully initialized.
  379. if(GdiplusInitialized)
  380. {
  381. // Retrieve the application name from the .rc file.
  382. LoadString(hMainInstance, idsAppName, szAppName, 40);
  383. // Initialize the global state.
  384. GetClientRect (hwnd, &rc);
  385. LoadState();
  386. switch(nTileSize)
  387. {
  388. // 1x1 pixel
  389. case 0:
  390. gSizeX = 1;
  391. gSizeY = 1;
  392. break;
  393. // Aspect ratio of 4x3, for pictures.
  394. case 1:
  395. gSizeX = 16;
  396. gSizeY = 12;
  397. break;
  398. case 2:
  399. gSizeX = 32;
  400. gSizeY = 24;
  401. break;
  402. case 3:
  403. gSizeX = 64;
  404. gSizeY = 48;
  405. break;
  406. case 4:
  407. gSizeX = 96;
  408. gSizeY = 72;
  409. break;
  410. }
  411. ghFile = 0;
  412. gGenerations = 400;
  413. gCurrentGeneration = 0;
  414. gWidth = (rc.right - rc.left + 1)/gSizeX;
  415. gHeight = (rc.bottom - rc.top + 1)/gSizeY;
  416. gLifeMatrix = (INT *)malloc(sizeof(INT)*gWidth*gHeight);
  417. gTempMatrix = (INT *)malloc(sizeof(INT)*gWidth*gHeight);
  418. if(nTileSize == 0)
  419. {
  420. // 1x1 tilesize case.
  421. CreateOffscreenDIB(
  422. hdc,
  423. rc.right - rc.left + 1,
  424. rc.bottom - rc.top + 1
  425. );
  426. red = rand() % 255;
  427. green = rand() % 255;
  428. blue = min(255, 512 - (red + green));
  429. ri = (rand() % 3) - 1; // 1, 0 or -1
  430. bi = (rand() % 3) - 1; // 1, 0 or -1
  431. gi = (rand() % 3) - 1; // 1, 0 or -1
  432. }
  433. else
  434. {
  435. // Image case.
  436. CachedImages = new CachedImageArray();
  437. }
  438. maxImage = CBSIZE;
  439. currentImage = 0; // initial number
  440. // Set a timer for the screen saver window
  441. uTimer = SetTimer(hwnd, 1, 1000, NULL);
  442. srand( (unsigned)GetTickCount() );
  443. }
  444. break;
  445. case WM_ERASEBKGND:
  446. // The WM_ERASEBKGND message is issued before the
  447. // WM_TIMER message, allowing the screen saver to
  448. // paint the background as appropriate.
  449. break;
  450. case WM_TIMER:
  451. // Only do work if we successfully initialized.
  452. if(GdiplusInitialized)
  453. {
  454. if (uTimer)
  455. {
  456. KillTimer(hwnd, uTimer);
  457. }
  458. hdc = GetDC(hwnd);
  459. GetClientRect(hwnd, &rc);
  460. DrawLifeIteration(hdc);
  461. uTimer = SetTimer(hwnd, 1, nSpeed*10, NULL);
  462. ReleaseDC(hwnd,hdc);
  463. }
  464. break;
  465. case WM_DESTROY:
  466. // When the WM_DESTROY message is issued, the screen saver
  467. // must destroy any of the timers that were set at WM_CREATE
  468. // time.
  469. // Only do work if we successfully initialized.
  470. if(GdiplusInitialized)
  471. {
  472. if (uTimer)
  473. {
  474. KillTimer(hwnd, uTimer);
  475. }
  476. free(gTempMatrix);
  477. free(gLifeMatrix);
  478. FindClose(ghFile);
  479. delete CachedImages;
  480. GdiplusShutdown(gpToken);
  481. GdiplusInitialized = false;
  482. }
  483. break;
  484. }
  485. // DefScreenSaverProc processes any messages ignored by ScreenSaverProc.
  486. #ifdef STANDALONE_DEBUG
  487. return DefWindowProc(hwnd, message, wParam, lParam);
  488. #else
  489. return DefScreenSaverProc(hwnd, message, wParam, lParam);
  490. #endif
  491. }
  492. #define TEMP(x, y) gTempMatrix[ ((x+gWidth) % gWidth) + ((y+gHeight) % gHeight)*gWidth ]
  493. #define LIFE(x, y) gLifeMatrix[ ((x+gWidth) % gWidth) + ((y+gHeight) % gHeight)*gWidth ]
  494. inline bool AliveT(int x, int y)
  495. {
  496. return (TEMP(x, y) & 0x1);
  497. }
  498. inline bool AliveL(int x, int y)
  499. {
  500. return (LIFE(x, y) & 0x1);
  501. }
  502. inline INT CountT(int x, int y)
  503. {
  504. return (TEMP(x, y) >> 1);
  505. }
  506. inline void NewCellL(INT x, INT y)
  507. {
  508. ASSERT(!AliveL(x, y));
  509. // update current cell
  510. LIFE(x, y) += 1;
  511. // update neighbour counts.
  512. LIFE(x-1, y-1) += 2;
  513. LIFE(x-1, y ) += 2;
  514. LIFE(x-1, y+1) += 2;
  515. LIFE(x , y-1) += 2;
  516. LIFE(x , y+1) += 2;
  517. LIFE(x+1, y-1) += 2;
  518. LIFE(x+1, y ) += 2;
  519. LIFE(x+1, y+1) += 2;
  520. }
  521. inline void NewCellL_NoWrap(INT index)
  522. {
  523. ASSERT(! (gLifeMatrix[index] & 0x1) );
  524. // update current cell
  525. gLifeMatrix[index] += 1;
  526. // update neighbour counts.
  527. gLifeMatrix[index - 1 - gWidth] += 2;
  528. gLifeMatrix[index - 1 ] += 2;
  529. gLifeMatrix[index - 1 + gWidth] += 2;
  530. gLifeMatrix[index - gWidth] += 2;
  531. gLifeMatrix[index + gWidth] += 2;
  532. gLifeMatrix[index + 1 - gWidth] += 2;
  533. gLifeMatrix[index + 1 ] += 2;
  534. gLifeMatrix[index + 1 + gWidth] += 2;
  535. }
  536. inline void KillCellL(INT x, INT y)
  537. {
  538. ASSERT(AliveL(x, y));
  539. // update current cell
  540. LIFE(x, y) -= 1;
  541. // update neighbour counts.
  542. LIFE(x-1, y-1) -= 2;
  543. LIFE(x-1, y ) -= 2;
  544. LIFE(x-1, y+1) -= 2;
  545. LIFE(x , y-1) -= 2;
  546. LIFE(x , y+1) -= 2;
  547. LIFE(x+1, y-1) -= 2;
  548. LIFE(x+1, y ) -= 2;
  549. LIFE(x+1, y+1) -= 2;
  550. }
  551. inline void KillCellL_NoWrap(INT index)
  552. {
  553. ASSERT(gLifeMatrix[index] & 0x1);
  554. // update current cell
  555. gLifeMatrix[index] -= 1;
  556. // update neighbour counts.
  557. gLifeMatrix[index - 1 - gWidth] -= 2;
  558. gLifeMatrix[index - 1 ] -= 2;
  559. gLifeMatrix[index - 1 + gWidth] -= 2;
  560. gLifeMatrix[index - gWidth] -= 2;
  561. gLifeMatrix[index + gWidth] -= 2;
  562. gLifeMatrix[index + 1 - gWidth] -= 2;
  563. gLifeMatrix[index + 1 ] -= 2;
  564. gLifeMatrix[index + 1 + gWidth] -= 2;
  565. }
  566. VOID InitLifeMatrix()
  567. {
  568. memset(gLifeMatrix, 0, sizeof(INT)*gWidth*gHeight);
  569. if(nTileSize == 0)
  570. {
  571. ClearOffscreenDIB();
  572. }
  573. if((rand()%2 == 0) ||
  574. (gWidth<b_heptomino_x) ||
  575. (gHeight<b_heptomino_y))
  576. {
  577. for(int i=1; i<gWidth-1; i++)
  578. for(int j=1; j<gHeight-1; j++)
  579. {
  580. if((rand() % 3) == 0)
  581. {
  582. NewCellL_NoWrap(i + j*gWidth);
  583. }
  584. }
  585. for(int i=0; i<gWidth; i++)
  586. {
  587. if((rand() % 3) == 0)
  588. {
  589. NewCellL(i, 0);
  590. }
  591. if((rand() % 3) == 0)
  592. {
  593. NewCellL(i, gHeight-1);
  594. }
  595. }
  596. for(int j=1; j<gHeight-1; j++)
  597. {
  598. if((rand() % 3) == 0)
  599. {
  600. NewCellL(0, j);
  601. }
  602. if((rand() % 3) == 0)
  603. {
  604. NewCellL(gWidth-1, j);
  605. }
  606. }
  607. }
  608. else
  609. {
  610. for(int i=0; i<b_heptomino_x; i++)
  611. for(int j=0; j<b_heptomino_y; j++)
  612. {
  613. if(b_heptomino[i+j*b_heptomino_x] != '0')
  614. {
  615. NewCellL(i, j);
  616. }
  617. }
  618. }
  619. }
  620. Bitmap *OpenBitmap()
  621. {
  622. Bitmap *bmp = NULL;
  623. WIN32_FIND_DATA findData = {0};
  624. WCHAR filename[1024];
  625. do
  626. {
  627. // don't leak if we repeat this loop due to an invalid bitmap.
  628. delete bmp; bmp = NULL;
  629. if(ghFile)
  630. {
  631. if(!FindNextFileW(ghFile, &findData))
  632. {
  633. // finished going through the list.
  634. FindClose(ghFile);
  635. ghFile = 0;
  636. maxImage = currentImage;
  637. currentImage = 0;
  638. return NULL;
  639. }
  640. }
  641. if(!ghFile)
  642. {
  643. currentImage = 0; // we're about to increment this.
  644. wsprintf(filename, L"%s\\*.*", gDirPath);
  645. ghFile = FindFirstFileW(filename, &findData);
  646. if(!ghFile)
  647. {
  648. return NULL; // No files.
  649. }
  650. }
  651. if((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  652. {
  653. wsprintf(
  654. filename,
  655. L"%s\\%s",
  656. gDirPath,
  657. findData.cFileName
  658. );
  659. bmp = new Bitmap(filename);
  660. }
  661. // !!! need to prevent infinite loops.
  662. } while (
  663. ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
  664. FILE_ATTRIBUTE_DIRECTORY) ||
  665. (bmp->GetLastStatus() != Ok)
  666. );
  667. return bmp;
  668. }
  669. VOID InitialPaintPicture(Graphics *g, CachedBitmap *cb)
  670. {
  671. SolidBrush OffBrush(Color(0xff000000));
  672. for(int x=0; x<gWidth; x++)
  673. {
  674. for(int y=0; y<gHeight; y++)
  675. {
  676. if(AliveL(x, y))
  677. {
  678. g->DrawCachedBitmap(
  679. cb,
  680. x*gSizeX,
  681. y*gSizeY
  682. );
  683. }
  684. else
  685. {
  686. // we should really use a bitblt for this.
  687. g->FillRectangle(
  688. &OffBrush,
  689. x*gSizeX,
  690. y*gSizeY,
  691. gSizeX,
  692. gSizeY
  693. );
  694. }
  695. }
  696. }
  697. }
  698. VOID InitialPaintPixel()
  699. {
  700. ASSERT(nTileSize == 0);
  701. DWORD *pixel = (DWORD*)(gOffscreenInfo.pvBits);
  702. ASSERT(pixel != NULL);
  703. for(int x=0; x<gWidth; x++)
  704. {
  705. for(int y=0; y<gHeight; y++)
  706. {
  707. // we know there's no wrapping, we shouldn't have to do the mod.
  708. INT index = x+y*gWidth;
  709. if(gLifeMatrix[index] & 0x1)
  710. {
  711. pixel[index] = gGenerationColor;
  712. }
  713. }
  714. }
  715. }
  716. CachedBitmap *MakeCachedBitmapEntry(Bitmap *bmp, Graphics *gfxMain)
  717. {
  718. // Make a tile bitmap and wrap a graphics around it.
  719. Bitmap *tileBmp = new Bitmap(gSizeX, gSizeY, PixelFormat32bppPARGB);
  720. Graphics *g = new Graphics(tileBmp);
  721. // Shrink the image down to the tile size using the Bicubic filter.
  722. g->SetInterpolationMode(InterpolationModeHighQualityBicubic);
  723. g->DrawImage(
  724. bmp,
  725. Rect(0,0,gSizeX,gSizeY),
  726. 0,
  727. 0,
  728. bmp->GetWidth(),
  729. bmp->GetHeight(),
  730. UnitPixel
  731. );
  732. // Create the CachedBitmap from the tile.
  733. CachedBitmap *cb = new CachedBitmap(tileBmp, gfxMain);
  734. // clean up.
  735. delete g;
  736. delete tileBmp;
  737. return cb;
  738. }
  739. VOID IteratePixelGeneration()
  740. {
  741. DWORD *pixel = (DWORD*)(gOffscreenInfo.pvBits);
  742. INT count;
  743. INT index = 1+gWidth;
  744. for(int y=1; y<gHeight-1; y++)
  745. {
  746. for(int x=1; x<gWidth-1; x++)
  747. {
  748. if(gTempMatrix[index] != 0)
  749. {
  750. // If the cell is not alive and it's neighbour count
  751. // is exactly 3, it is born.
  752. if( gTempMatrix[index] == 6 )
  753. {
  754. // A new cell is born into an empty square.
  755. NewCellL_NoWrap(index);
  756. pixel[index] = gGenerationColor;
  757. }
  758. else
  759. {
  760. count = gTempMatrix[index] >> 1;
  761. // if the cell is alive and its neighbour count
  762. // is not 2 or 3, it dies.
  763. if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
  764. {
  765. // Kill the cell - overcrowding or not enough support.
  766. KillCellL_NoWrap(index);
  767. pixel[index] = 0;
  768. }
  769. }
  770. }
  771. index++;
  772. }
  773. // skip the wrap boundaries.
  774. index += 2;
  775. }
  776. index = 0;
  777. for(int y=0; y<gHeight; y++)
  778. {
  779. // left vertical edge.
  780. if(gTempMatrix[index] != 0)
  781. {
  782. // If the cell is not alive and it's neighbour count
  783. // is exactly 3, it is born.
  784. if( gTempMatrix[index] == 6 )
  785. {
  786. // A new cell is born into an empty square.
  787. NewCellL(0, y);
  788. pixel[index] = gGenerationColor;
  789. }
  790. else
  791. {
  792. count = gTempMatrix[index] >> 1;
  793. // if the cell is alive and its neighbour count
  794. // is not 2 or 3, it dies.
  795. if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
  796. {
  797. // Kill the cell - overcrowding or not enough support.
  798. KillCellL(0, y);
  799. pixel[index] = 0;
  800. }
  801. }
  802. }
  803. // right vertical edge
  804. index += gWidth-1;
  805. if(gTempMatrix[index] != 0)
  806. {
  807. // If the cell is not alive and it's neighbour count
  808. // is exactly 3, it is born.
  809. if( gTempMatrix[index] == 6 )
  810. {
  811. // A new cell is born into an empty square.
  812. NewCellL(gWidth-1, y);
  813. pixel[index] = gGenerationColor;
  814. }
  815. else
  816. {
  817. count = gTempMatrix[index] >> 1;
  818. // if the cell is alive and its neighbour count
  819. // is not 2 or 3, it dies.
  820. if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
  821. {
  822. // Kill the cell - overcrowding or not enough support.
  823. KillCellL(gWidth-1, y);
  824. pixel[index] = 0;
  825. }
  826. }
  827. }
  828. // next scanline.
  829. index++;
  830. }
  831. index = 1;
  832. INT index2 = index + (gHeight-1)*gWidth;
  833. for(int x=1; x<gWidth-1; x++)
  834. {
  835. // top edge.
  836. if(gTempMatrix[index] != 0)
  837. {
  838. // If the cell is not alive and it's neighbour count
  839. // is exactly 3, it is born.
  840. if( gTempMatrix[index] == 6 )
  841. {
  842. // A new cell is born into an empty square.
  843. NewCellL(x, 0);
  844. pixel[index] = gGenerationColor;
  845. }
  846. else
  847. {
  848. count = gTempMatrix[index] >> 1;
  849. // if the cell is alive and its neighbour count
  850. // is not 2 or 3, it dies.
  851. if( (gTempMatrix[index] & 0x1) && ((count<2) || (count>3)) )
  852. {
  853. // Kill the cell - overcrowding or not enough support.
  854. KillCellL(x, 0);
  855. pixel[index] = 0;
  856. }
  857. }
  858. }
  859. index++;
  860. // bottom edge
  861. if(gTempMatrix[index2] != 0)
  862. {
  863. // If the cell is not alive and it's neighbour count
  864. // is exactly 3, it is born.
  865. if( gTempMatrix[index2] == 6 )
  866. {
  867. // A new cell is born into an empty square.
  868. NewCellL(x, gHeight-1);
  869. pixel[index2] = gGenerationColor;
  870. }
  871. else
  872. {
  873. count = gTempMatrix[index2] >> 1;
  874. // if the cell is alive and its neighbour count
  875. // is not 2 or 3, it dies.
  876. if( (gTempMatrix[index2] & 0x1) && ((count<2) || (count>3)) )
  877. {
  878. // Kill the cell - overcrowding or not enough support.
  879. KillCellL(x, gHeight-1);
  880. pixel[index2] = 0;
  881. }
  882. }
  883. }
  884. // next pixel.
  885. index2++;
  886. }
  887. }
  888. VOID IteratePictureGeneration(Graphics &g, CachedBitmap *cb)
  889. {
  890. SolidBrush OffBrush(Color(0xff000000));
  891. INT count;
  892. INT *cell = gTempMatrix;
  893. for(int y=0; y<gHeight; y++)
  894. {
  895. for(int x=0; x<gWidth; x++)
  896. {
  897. if(*cell != 0)
  898. {
  899. // If the cell is not alive and it's neighbour count
  900. // is exactly 3, it is born.
  901. if( *cell == 6 )
  902. {
  903. // A new cell is born into an empty square.
  904. NewCellL(x, y);
  905. g.DrawCachedBitmap(
  906. cb,
  907. x*gSizeX,
  908. y*gSizeY
  909. );
  910. }
  911. else
  912. {
  913. count = *cell >> 1;
  914. // if the cell is alive and its neighbour count
  915. // is not 2 or 3, it dies.
  916. if( (*cell & 0x1) && ((count<2) || (count>3)) )
  917. {
  918. // Kill the cell - overcrowding or not enough support.
  919. KillCellL(x, y);
  920. g.FillRectangle(
  921. &OffBrush,
  922. x*gSizeX,
  923. y*gSizeY,
  924. gSizeX,
  925. gSizeY
  926. );
  927. }
  928. }
  929. }
  930. cell++;
  931. }
  932. }
  933. }
  934. VOID RandomizeColor()
  935. {
  936. if(rand() % 200 == 0)
  937. {
  938. ri = (rand() % 3) - 1; // 1, 0 or -1
  939. }
  940. if(rand() % 200 == 0)
  941. {
  942. gi = (rand() % 3) - 1; // 1, 0 or -1
  943. }
  944. if(rand() % 200 == 0)
  945. {
  946. bi = (rand() % 3) - 1; // 1, 0 or -1
  947. }
  948. if((red < 100) && (green < 100) && (blue < 100))
  949. {
  950. if(red > green && red > blue)
  951. {
  952. ri = 1;
  953. }
  954. else if (green > blue)
  955. {
  956. gi = 1;
  957. }
  958. else
  959. {
  960. bi = 1;
  961. }
  962. }
  963. // bounce off the extrema.
  964. if(red == 0)
  965. {
  966. ri = 1;
  967. }
  968. if(red == 255)
  969. {
  970. ri = -1;
  971. }
  972. if(green == 0)
  973. {
  974. gi = 1;
  975. }
  976. if(green == 255)
  977. {
  978. gi = -1;
  979. }
  980. if(blue == 0)
  981. {
  982. bi = 1;
  983. }
  984. if(blue == 255)
  985. {
  986. bi = -1;
  987. }
  988. red += ri;
  989. green += gi;
  990. blue += bi;
  991. }
  992. VOID DrawLifeIteration(HDC hdc)
  993. {
  994. // Are we initialized yet?
  995. if(!gLifeMatrix || !gTempMatrix) { return; }
  996. Graphics g(hdc);
  997. g.SetSmoothingMode(SmoothingModeNone);
  998. Bitmap *bmp = NULL;
  999. CachedBitmap *cb = NULL;
  1000. // currentImage should never be larger than CBSIZE at this point.
  1001. ASSERT(currentImage < CBSIZE);
  1002. if(nTileSize==0)
  1003. {
  1004. // cycle color.
  1005. RandomizeColor();
  1006. gGenerationColor = RGB(red, green, blue);
  1007. }
  1008. else
  1009. {
  1010. // Fetch bitmaps from the image directory.
  1011. if(currentImage >= CachedImages->Size()) {
  1012. // We haven't filled up the cache yet. Keep opening images.
  1013. bmp = OpenBitmap();
  1014. }
  1015. // Did we get a new bitmap?
  1016. if(bmp)
  1017. {
  1018. cb = MakeCachedBitmapEntry(bmp, &g);
  1019. if(cb)
  1020. {
  1021. // Put it in the cache.
  1022. CachedImages->Add(cb);
  1023. currentImage++;
  1024. }
  1025. delete bmp; bmp = NULL;
  1026. }
  1027. else
  1028. {
  1029. cb = (*CachedImages)[currentImage];
  1030. currentImage++;
  1031. }
  1032. if( (currentImage >= CBSIZE) ||
  1033. (currentImage >= maxImage) )
  1034. {
  1035. currentImage = 0;
  1036. }
  1037. if(!cb)
  1038. {
  1039. // we failed to get an image tile.
  1040. return;
  1041. }
  1042. }
  1043. // update the generation and see if we need to do the first generation.
  1044. //gCurrentGeneration--;
  1045. if(gCurrentGeneration <= 0)
  1046. {
  1047. // gCurrentGeneration = gGenerations;
  1048. gCurrentGeneration++;
  1049. InitLifeMatrix();
  1050. if(nTileSize == 0)
  1051. {
  1052. InitialPaintPixel();
  1053. }
  1054. else
  1055. {
  1056. InitialPaintPicture(&g, cb);
  1057. }
  1058. goto Done;
  1059. }
  1060. gCurrentGeneration++;
  1061. // Make a copy of the life matrix.
  1062. memcpy(gTempMatrix, gLifeMatrix, sizeof(INT)*gWidth*gHeight);
  1063. if(nTileSize==0)
  1064. {
  1065. IteratePixelGeneration();
  1066. ASSERT(gSizeX == 1);
  1067. ASSERT(gSizeY == 1);
  1068. StretchBlt(
  1069. hdc,
  1070. 0,
  1071. 0,
  1072. gWidth,
  1073. gHeight,
  1074. gOffscreenInfo.hdc,
  1075. 0,
  1076. 0,
  1077. gWidth,
  1078. gHeight,
  1079. SRCCOPY
  1080. );
  1081. }
  1082. else
  1083. {
  1084. IteratePictureGeneration(g, cb);
  1085. }
  1086. // 5% mutation.
  1087. /*
  1088. if(((float)(rand())/RAND_MAX) < 0.05f)
  1089. {
  1090. int x = rand()*gWidth/RAND_MAX;
  1091. int y = rand()*gHeight/RAND_MAX;
  1092. if(AliveL(x, y))
  1093. {
  1094. KillCellL(x, y);
  1095. g.FillRectangle(
  1096. &OffBrush,
  1097. x*gSizeX,
  1098. y*gSizeY,
  1099. gSizeX,
  1100. gSizeY
  1101. );
  1102. }
  1103. else
  1104. {
  1105. NewCellL(x, y);
  1106. g.DrawCachedBitmap(
  1107. cb,
  1108. x*gSizeX,
  1109. y*gSizeY
  1110. );
  1111. }
  1112. }
  1113. */
  1114. Done:
  1115. ;
  1116. }
  1117. #ifdef STANDALONE_DEBUG
  1118. LONG_PTR
  1119. lMainWindowProc(
  1120. HWND hwnd,
  1121. UINT message,
  1122. WPARAM wParam,
  1123. LPARAM lParam
  1124. )
  1125. {
  1126. switch(message)
  1127. {
  1128. // Handle the destroy message.
  1129. case WM_DESTROY:
  1130. DeleteObject(ghbrWhite);
  1131. PostQuitMessage(0);
  1132. break;
  1133. }
  1134. // Hook into the screen saver windproc.
  1135. return(ScreenSaverProcW(hwnd, message, wParam, lParam));
  1136. }
  1137. BOOL bInitApp(VOID)
  1138. {
  1139. WNDCLASS wc;
  1140. // not quite so white background brush.
  1141. ghbrWhite = CreateSolidBrush(RGB(0xFF,0xFF,0xFF));
  1142. wc.style = 0;
  1143. wc.lpfnWndProc = lMainWindowProc;
  1144. wc.cbClsExtra = 0;
  1145. wc.cbWndExtra = 0;
  1146. wc.hInstance = hMainInstance;
  1147. wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  1148. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  1149. wc.hbrBackground = ghbrWhite;
  1150. wc.lpszMenuName = L"MainMenu";
  1151. wc.lpszClassName = L"TestClass";
  1152. if(!RegisterClass(&wc)) { return FALSE; }
  1153. ghwndMain = CreateWindowExW(
  1154. 0,
  1155. L"TestClass",
  1156. L"Win32 Test",
  1157. WS_OVERLAPPED |
  1158. WS_CAPTION |
  1159. WS_BORDER |
  1160. WS_THICKFRAME |
  1161. WS_MAXIMIZEBOX |
  1162. WS_MINIMIZEBOX |
  1163. WS_CLIPCHILDREN |
  1164. WS_VISIBLE |
  1165. WS_SYSMENU,
  1166. 80,
  1167. 70,
  1168. 800,
  1169. 600,
  1170. NULL,
  1171. NULL,
  1172. hMainInstance,
  1173. NULL
  1174. );
  1175. if (ghwndMain == NULL)
  1176. {
  1177. return(FALSE);
  1178. }
  1179. SetFocus(ghwndMain);
  1180. return TRUE;
  1181. }
  1182. void _cdecl main(
  1183. INT argc,
  1184. PCHAR argv[]
  1185. )
  1186. {
  1187. MSG msg;
  1188. hMainInstance = GetModuleHandle(NULL);
  1189. if(!bInitApp()) {return;}
  1190. while(GetMessage (&msg, NULL, 0, 0))
  1191. {
  1192. if((ghwndMain == 0) || !IsDialogMessage(ghwndMain, &msg)) {
  1193. TranslateMessage(&msg) ;
  1194. DispatchMessage(&msg) ;
  1195. }
  1196. }
  1197. return;
  1198. UNREF(argc);
  1199. UNREF(argv);
  1200. }
  1201. #endif