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.

1008 lines
20 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1999 Microsoft Corporation
  4. *
  5. * Module Name:
  6. *
  7. * warpdemo.cxx
  8. *
  9. * Abstract:
  10. *
  11. * Image warping demo program
  12. *
  13. * Usage:
  14. * warpdemo bitmapfile
  15. *
  16. * Keystrokes:
  17. * SPACE - show/hide mesh
  18. * r - reset mesh to default
  19. * 1 - restore 1-to-1 scale
  20. * < - decrease mesh density
  21. * > - increase mesh density
  22. * f - toggle realtime feedback
  23. *
  24. * Revision History:
  25. *
  26. * 01/18/1999 davidx
  27. * Created it.
  28. *
  29. \**************************************************************************/
  30. #include "precomp.hxx"
  31. CHAR* programName; // program name
  32. HINSTANCE appInstance; // handle to the application instance
  33. HWND hwndMain; // handle to application's main window
  34. SIZE srcSize; // source bitmap size
  35. PVOID srcBmpData; // source bitmap data
  36. INT srcStride; // source scanline stride
  37. SIZE dstSize; // destination bitmap size
  38. PVOID dstBmpData; // destination bitmap data
  39. INT dstStride; // destination scanline stride
  40. SIZE wndSizeExtra; // extra pixels for window decorations
  41. BOOL isDragging = FALSE; // used to handle mouse dragging
  42. INT dragRow, dragCol; // the control knob being dragged
  43. BOOL clearWarpCache; // is the cached warping result valid?
  44. BOOL liveFeedback = FALSE; // realtime feedback while dragging mesh?
  45. INT knobSize; // mesh control point knob size
  46. #define MIN_KNOB_SIZE 4
  47. #define MAX_KNOB_SIZE 7
  48. #define MIN_MESH_GRID 3
  49. #define MAX_MESH_GRID 32
  50. #define DEFAULT_MESH_GRID 9
  51. INT meshGrids = DEFAULT_MESH_GRID;
  52. Mesh* mesh = NULL;
  53. BOOL showMesh = TRUE;
  54. //
  55. // Display an error message dialog and quit
  56. //
  57. VOID
  58. Error(
  59. const CHAR* fmt,
  60. ...
  61. )
  62. {
  63. va_list arglist;
  64. va_start(arglist, fmt);
  65. vfprintf(stderr, fmt, arglist);
  66. va_end(arglist);
  67. exit(-1);
  68. }
  69. //
  70. // Create a new mesh object
  71. //
  72. VOID
  73. CreateMesh()
  74. {
  75. mesh = new Mesh(meshGrids, meshGrids);
  76. if (mesh == NULL)
  77. Error("Couldn't create Mesh object\n");
  78. clearWarpCache = TRUE;
  79. }
  80. //
  81. // Calculate mesh control point knob size based
  82. // on current window width and height and also
  83. // the number of mesh grids.
  84. //
  85. VOID
  86. CalcKnobSize(
  87. INT width,
  88. INT height
  89. )
  90. {
  91. width /= (meshGrids-1);
  92. height /= (meshGrids-1);
  93. knobSize = min(width, height) / 5;
  94. if (knobSize < MIN_KNOB_SIZE)
  95. knobSize = MIN_KNOB_SIZE;
  96. else if (knobSize > MAX_KNOB_SIZE)
  97. knobSize = MAX_KNOB_SIZE;
  98. }
  99. //
  100. // Perform image warping operation based on current mesh configuration
  101. //
  102. HDC hdcWarp = NULL;
  103. HBITMAP hbmpWarp = NULL;
  104. VOID
  105. DoWarp(
  106. INT width,
  107. INT height
  108. )
  109. {
  110. // Uncache any previous warping results
  111. if (hdcWarp)
  112. DeleteDC(hdcWarp);
  113. if (hbmpWarp)
  114. DeleteObject(hbmpWarp);
  115. // Create offscreen DC to cache warping results
  116. dstSize.cx = width;
  117. dstSize.cy = height;
  118. dstStride = ((width * PIXELSIZE) + 3) & ~3;
  119. BITMAPINFOHEADER header =
  120. {
  121. sizeof(header),
  122. dstSize.cx,
  123. -dstSize.cy,
  124. 1,
  125. PIXELSIZE*8,
  126. BI_RGB,
  127. };
  128. hdcWarp = CreateCompatibleDC(NULL);
  129. hbmpWarp = CreateDIBSection(
  130. NULL,
  131. (BITMAPINFO*) &header,
  132. DIB_RGB_COLORS,
  133. &dstBmpData,
  134. NULL,
  135. 0);
  136. if (!hdcWarp || !hbmpWarp)
  137. Error("Couldn't create DC to cache warping results\n");
  138. SelectObject(hdcWarp, hbmpWarp);
  139. // Horizontal pass
  140. PVOID tmpBmpData;
  141. INT x, y;
  142. double* outpos;
  143. tmpBmpData = malloc(dstStride*(srcSize.cy + 2));
  144. outpos = (double*) malloc(sizeof(double) * (max(srcSize.cx, srcSize.cy) + 1));
  145. if (!tmpBmpData || !outpos)
  146. Error("Could allocate temporary memory for warping\n");
  147. MeshIterator* iterator;
  148. iterator = mesh->getYIterator(srcSize.cx, dstSize.cx, srcSize.cy);
  149. for (y=0; y < srcSize.cy; y++)
  150. {
  151. // compute the output position for each
  152. iterator->getOutPos(y, outpos);
  153. // perform 1D resampling
  154. Resample1D(
  155. (PBYTE) srcBmpData + y*srcStride,
  156. srcSize.cx,
  157. (PBYTE) tmpBmpData + y*dstStride,
  158. dstSize.cx,
  159. PIXELSIZE,
  160. outpos);
  161. }
  162. delete iterator;
  163. // Vertical pass
  164. iterator = mesh->getXIterator(srcSize.cy, dstSize.cy, dstSize.cx);
  165. for (x=0; x < dstSize.cx; x++)
  166. {
  167. // compute the output position for each
  168. iterator->getOutPos(x, outpos);
  169. // perform 1D resampling
  170. Resample1D(
  171. (PBYTE) tmpBmpData + PIXELSIZE*x,
  172. srcSize.cy,
  173. (PBYTE) dstBmpData + PIXELSIZE*x,
  174. dstSize.cy,
  175. dstStride,
  176. outpos);
  177. }
  178. delete iterator;
  179. free(tmpBmpData);
  180. free(outpos);
  181. }
  182. //
  183. // Draw mesh
  184. //
  185. #define MESHCOLOR RGB(255, 0, 0)
  186. VOID
  187. DrawMesh(
  188. HDC hdc
  189. )
  190. {
  191. static HPEN meshPen = NULL;
  192. static HBRUSH meshBrush = NULL;
  193. mesh->setDstSize(dstSize.cx, dstSize.cy);
  194. // Create the pen to draw the mesh, if necessary
  195. if (meshPen == NULL)
  196. meshPen = CreatePen(PS_SOLID, 1, MESHCOLOR);
  197. SelectObject(hdc, meshPen);
  198. // Draw horizontal meshes
  199. INT i, j, rows, cols, pointCount;
  200. POINT* points;
  201. rows = mesh->getGridRows();
  202. for (i=0; i < rows; i++)
  203. {
  204. points = mesh->getMeshRowBeziers(i, &pointCount);
  205. PolyBezier(hdc, points, pointCount);
  206. }
  207. // Draw vertical meshes
  208. cols = mesh->getGridColumns();
  209. for (i=0; i < cols; i++)
  210. {
  211. points = mesh->getMeshColumnBeziers(i, &pointCount);
  212. PolyBezier(hdc, points, pointCount);
  213. }
  214. // Draw knobs
  215. // Create the brush to draw the mesh if necessary
  216. if (meshBrush == NULL)
  217. meshBrush = CreateSolidBrush(MESHCOLOR);
  218. for (i=0; i < rows; i++)
  219. {
  220. points = mesh->getMeshRowPoints(i, &pointCount);
  221. for (j=0; j < cols; j++)
  222. {
  223. RECT rect;
  224. rect.left = points[j].x - knobSize/2;
  225. rect.top = points[j].y - knobSize/2;
  226. rect.right = rect.left + knobSize;
  227. rect.bottom = rect.top + knobSize;
  228. FillRect(hdc, &rect, meshBrush);
  229. }
  230. }
  231. }
  232. //
  233. // Handle window repaint event
  234. //
  235. VOID
  236. DoPaint(
  237. HWND hwnd
  238. )
  239. {
  240. HDC hdc;
  241. PAINTSTRUCT ps;
  242. RECT rect;
  243. INT width, height;
  244. // Determine if we need to perform warping operation
  245. GetClientRect(hwnd, &rect);
  246. width = rect.right;
  247. height = rect.bottom;
  248. if (clearWarpCache ||
  249. dstSize.cx != width ||
  250. dstSize.cy != height)
  251. {
  252. CalcKnobSize(width, height);
  253. clearWarpCache = FALSE;
  254. DoWarp(width, height);
  255. }
  256. hdc = BeginPaint(hwnd, &ps);
  257. if (showMesh)
  258. {
  259. // Draw to offscreen DC to reduce flashing
  260. HDC hdcMem;
  261. HBITMAP hbmp;
  262. hdcMem = CreateCompatibleDC(hdc);
  263. hbmp = CreateCompatibleBitmap(hdc, width, height);
  264. SelectObject(hdcMem, hbmp);
  265. BitBlt(hdcMem, 0, 0, width, height, hdcWarp, 0, 0, SRCCOPY);
  266. DrawMesh(hdcMem);
  267. // Blt from offscreen memory to window
  268. BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
  269. DeleteDC(hdcMem);
  270. DeleteObject(hbmp);
  271. }
  272. else
  273. {
  274. // Blt cached warping result to window
  275. BitBlt(hdc, 0, 0, width, height, hdcWarp, 0, 0, SRCCOPY);
  276. }
  277. EndPaint(hwnd, &ps);
  278. }
  279. //
  280. // Handle WM_SIZING message
  281. //
  282. BOOL
  283. DoWindowSizing(
  284. HWND hwnd,
  285. RECT* rect,
  286. INT side
  287. )
  288. {
  289. INT w = rect->right - rect->left - wndSizeExtra.cx;
  290. INT h = rect->bottom - rect->top - wndSizeExtra.cy;
  291. if (w >= srcSize.cx && h >= srcSize.cy)
  292. return FALSE;
  293. // Window width is too small
  294. if (w < srcSize.cx)
  295. {
  296. INT dx = srcSize.cx + wndSizeExtra.cx;
  297. switch (side)
  298. {
  299. case WMSZ_LEFT:
  300. case WMSZ_TOPLEFT:
  301. case WMSZ_BOTTOMLEFT:
  302. rect->left = rect->right - dx;
  303. break;
  304. default:
  305. rect->right = rect->left + dx;
  306. break;
  307. }
  308. }
  309. // Window height is too small
  310. if (h < srcSize.cy)
  311. {
  312. INT dy = srcSize.cy + wndSizeExtra.cy;
  313. switch (side)
  314. {
  315. case WMSZ_TOP:
  316. case WMSZ_TOPLEFT:
  317. case WMSZ_TOPRIGHT:
  318. rect->top = rect->bottom - dy;
  319. break;
  320. default:
  321. rect->bottom = rect->top + dy;
  322. break;
  323. }
  324. }
  325. return TRUE;
  326. }
  327. //
  328. // Handle left mouse-down event
  329. //
  330. VOID
  331. DoMouseDown(
  332. HWND hwnd,
  333. INT x,
  334. INT y
  335. )
  336. {
  337. // Figure out if the click happened in a mesh control knob
  338. INT i, j, rows, cols;
  339. POINT pt;
  340. RECT rect;
  341. GetClientRect(hwnd, &rect);
  342. mesh->setDstSize(rect.right, rect.bottom);
  343. rows = mesh->getGridRows();
  344. cols = mesh->getGridColumns();
  345. for (i=0; i < rows; i++)
  346. for (j=0; j < cols; j++)
  347. {
  348. mesh->getMeshPoint(i, j, &pt);
  349. pt.x -= knobSize/2;
  350. pt.y -= knobSize/2;
  351. if (x >= pt.x && x < pt.x+knobSize &&
  352. y >= pt.y && y < pt.y+knobSize)
  353. {
  354. dragRow = i;
  355. dragCol = j;
  356. SetCapture(hwnd);
  357. isDragging = TRUE;
  358. return;
  359. }
  360. }
  361. }
  362. //
  363. // Handle mouse-move event
  364. //
  365. VOID
  366. DoMouseMove(
  367. HWND hwnd,
  368. INT x,
  369. INT y
  370. )
  371. {
  372. // We assume isDragging is true here.
  373. RECT rect;
  374. INT w, h;
  375. GetClientRect(hwnd, &rect);
  376. w = rect.right;
  377. h = rect.bottom;
  378. if (x < 0 || x >= w || y < 0 || y >= h)
  379. return;
  380. mesh->setDstSize(w, h);
  381. if (mesh->setMeshPoint(dragRow, dragCol, x, y))
  382. {
  383. if (liveFeedback)
  384. clearWarpCache = TRUE;
  385. InvalidateRect(hwnd, NULL, FALSE);
  386. }
  387. }
  388. //
  389. // Handle menu command
  390. //
  391. VOID
  392. DoCommand(
  393. HWND hwnd,
  394. INT command
  395. )
  396. {
  397. switch (command)
  398. {
  399. case IDC_RESETMESH:
  400. mesh->initMesh();
  401. clearWarpCache = TRUE;
  402. break;
  403. case IDC_TOGGLEMESH:
  404. showMesh = !showMesh;
  405. break;
  406. case IDC_SHRINKTOFIT:
  407. SetWindowPos(
  408. hwnd, NULL, 0, 0,
  409. srcSize.cx + wndSizeExtra.cx,
  410. srcSize.cy + wndSizeExtra.cy,
  411. SWP_NOOWNERZORDER|SWP_NOMOVE);
  412. break;
  413. case IDC_DENSEMESH:
  414. if (meshGrids >= MAX_MESH_GRID)
  415. return;
  416. meshGrids++;
  417. CreateMesh();
  418. showMesh = TRUE;
  419. break;
  420. case IDC_SPARSEMESH:
  421. if (meshGrids <= MIN_MESH_GRID)
  422. return;
  423. meshGrids--;
  424. CreateMesh();
  425. showMesh = TRUE;
  426. break;
  427. case IDC_LIVEFEEDBACK:
  428. liveFeedback = !liveFeedback;
  429. return;
  430. default:
  431. return;
  432. }
  433. InvalidateRect(hwnd, NULL, FALSE);
  434. }
  435. //
  436. // Handle popup menu
  437. //
  438. VOID
  439. DoPopupMenu(
  440. HWND hwnd,
  441. INT x,
  442. INT y
  443. )
  444. {
  445. HMENU menu;
  446. DWORD result;
  447. POINT pt;
  448. GetCursorPos(&pt);
  449. menu = LoadMenu(appInstance, MAKEINTRESOURCE(IDM_MAINMENU));
  450. result = TrackPopupMenu(
  451. GetSubMenu(menu, 0),
  452. TPM_CENTERALIGN | TPM_TOPALIGN |
  453. TPM_NONOTIFY | TPM_RETURNCMD |
  454. TPM_RIGHTBUTTON,
  455. pt.x,
  456. pt.y,
  457. 0,
  458. hwnd,
  459. NULL);
  460. if (result == 0)
  461. return;
  462. DoCommand(hwnd, LOWORD(result));
  463. }
  464. //
  465. // Window callback procedure
  466. //
  467. LRESULT CALLBACK
  468. MyWindowProc(
  469. HWND hwnd,
  470. UINT uMsg,
  471. WPARAM wParam,
  472. LPARAM lParam
  473. )
  474. {
  475. INT x, y;
  476. switch (uMsg)
  477. {
  478. case WM_PAINT:
  479. DoPaint(hwnd);
  480. break;
  481. case WM_LBUTTONDOWN:
  482. if (showMesh)
  483. {
  484. x = (SHORT) LOWORD(lParam);
  485. y = (SHORT) HIWORD(lParam);
  486. DoMouseDown(hwnd, x, y);
  487. }
  488. break;
  489. case WM_LBUTTONUP:
  490. if (isDragging)
  491. {
  492. ReleaseCapture();
  493. isDragging = FALSE;
  494. clearWarpCache = TRUE;
  495. InvalidateRect(hwnd, NULL, FALSE);
  496. }
  497. break;
  498. case WM_MOUSEMOVE:
  499. if (isDragging)
  500. {
  501. x = (SHORT) LOWORD(lParam);
  502. y = (SHORT) HIWORD(lParam);
  503. DoMouseMove(hwnd, x, y);
  504. }
  505. break;
  506. case WM_SIZING:
  507. if (DoWindowSizing(hwnd, (RECT*) lParam, wParam))
  508. return TRUE;
  509. else
  510. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  511. case WM_SIZE:
  512. InvalidateRect(hwnd, NULL, FALSE);
  513. break;
  514. case WM_CHAR:
  515. switch ((CHAR) wParam)
  516. {
  517. case 'r': // reset
  518. DoCommand(hwnd, IDC_RESETMESH);
  519. break;
  520. case ' ': // show/hide mesh
  521. DoCommand(hwnd, IDC_TOGGLEMESH);
  522. break;
  523. case '1': // restore 1-to-1 scale
  524. DoCommand(hwnd, IDC_SHRINKTOFIT);
  525. break;
  526. case '<': // decrease mesh density
  527. DoCommand(hwnd, IDC_SPARSEMESH);
  528. break;
  529. case '>': // increase mesh density
  530. DoCommand(hwnd, IDC_DENSEMESH);
  531. break;
  532. case 'f': // toggle live feedback
  533. DoCommand(hwnd, IDC_LIVEFEEDBACK);
  534. break;
  535. }
  536. break;
  537. case WM_RBUTTONDOWN:
  538. x = (SHORT) LOWORD(lParam);
  539. y = (SHORT) HIWORD(lParam);
  540. DoPopupMenu(hwnd, x, y);
  541. break;
  542. case WM_DESTROY:
  543. PostQuitMessage(0);
  544. break;
  545. default:
  546. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  547. }
  548. return 0;
  549. }
  550. //
  551. // Create main application window
  552. //
  553. VOID
  554. CreateMainWindow(
  555. VOID
  556. )
  557. #define MYWNDCLASSNAME "WarpDemo"
  558. {
  559. //
  560. // Register window class if necessary
  561. //
  562. static BOOL wndclassRegistered = FALSE;
  563. if (!wndclassRegistered)
  564. {
  565. WNDCLASS wndClass =
  566. {
  567. 0,
  568. MyWindowProc,
  569. 0,
  570. 0,
  571. appInstance,
  572. LoadIcon(NULL, IDI_APPLICATION),
  573. LoadCursor(NULL, IDC_ARROW),
  574. NULL,
  575. NULL,
  576. MYWNDCLASSNAME
  577. };
  578. RegisterClass(&wndClass);
  579. wndclassRegistered = TRUE;
  580. }
  581. wndSizeExtra.cx = 2*GetSystemMetrics(SM_CXSIZEFRAME);
  582. wndSizeExtra.cy = 2*GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);
  583. hwndMain = CreateWindow(
  584. MYWNDCLASSNAME,
  585. MYWNDCLASSNAME,
  586. WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  587. CW_USEDEFAULT,
  588. CW_USEDEFAULT,
  589. srcSize.cx + wndSizeExtra.cx,
  590. srcSize.cy + wndSizeExtra.cy,
  591. NULL,
  592. NULL,
  593. appInstance,
  594. NULL);
  595. }
  596. //
  597. // Map a file into process memory space
  598. //
  599. PVOID
  600. MapFileIntoMemory(
  601. PCSTR filename,
  602. DWORD* size
  603. )
  604. {
  605. HANDLE filehandle, filemap;
  606. PVOID fileview = NULL;
  607. //
  608. // Open a handle to the specified file
  609. //
  610. filehandle = CreateFile(
  611. filename,
  612. GENERIC_READ,
  613. FILE_SHARE_READ,
  614. NULL,
  615. OPEN_EXISTING,
  616. FILE_ATTRIBUTE_NORMAL||FILE_FLAG_SEQUENTIAL_SCAN,
  617. NULL);
  618. if (filehandle == INVALID_HANDLE_VALUE)
  619. return NULL;
  620. //
  621. // Obtain the file size
  622. //
  623. *size = GetFileSize(filehandle, NULL);
  624. if (*size == 0xFFFFFFFF)
  625. {
  626. CloseHandle(filehandle);
  627. return NULL;
  628. }
  629. //
  630. // Map the file into memory
  631. //
  632. filemap = CreateFileMapping(filehandle, NULL, PAGE_READONLY, 0, 0, NULL);
  633. if (filemap != NULL)
  634. {
  635. fileview = MapViewOfFile(filemap, FILE_MAP_READ, 0, 0, 0);
  636. CloseHandle(filemap);
  637. }
  638. CloseHandle(filehandle);
  639. return fileview;
  640. }
  641. //
  642. // Load source bitmap file
  643. //
  644. VOID
  645. LoadBitmapFile(
  646. PCSTR filename
  647. )
  648. {
  649. BITMAPFILEHEADER* bmpfile;
  650. BITMAPINFO* bmpinfo;
  651. PBYTE bmpdata;
  652. HBITMAP dibSection = NULL;
  653. DWORD filesize;
  654. PVOID fileview = NULL;
  655. HDC hdc = NULL;
  656. BOOL success = FALSE;
  657. __try
  658. {
  659. //
  660. // Map the bitmap file into memory
  661. //
  662. fileview = MapFileIntoMemory(filename, &filesize);
  663. if (fileview == NULL)
  664. __leave;
  665. bmpfile = (BITMAPFILEHEADER *) fileview;
  666. bmpinfo = (BITMAPINFO *) ((PBYTE) fileview + sizeof(BITMAPFILEHEADER));
  667. bmpdata = (PBYTE) fileview + bmpfile->bfOffBits;
  668. //
  669. // Check bitmap file header information
  670. //
  671. if (bmpfile->bfType != 0x4D42 || // 'BM'
  672. bmpfile->bfSize > filesize ||
  673. bmpfile->bfOffBits >= bmpfile->bfSize)
  674. {
  675. __leave;
  676. }
  677. //
  678. // Allocate memory for source bitmap
  679. //
  680. srcSize.cx = bmpinfo->bmiHeader.biWidth;
  681. srcSize.cy = abs(bmpinfo->bmiHeader.biHeight);
  682. srcStride = ((srcSize.cx * PIXELSIZE) + 3) & ~3;
  683. BITMAPINFOHEADER header =
  684. {
  685. sizeof(header),
  686. srcSize.cx,
  687. -srcSize.cy,
  688. 1,
  689. PIXELSIZE*8,
  690. BI_RGB,
  691. };
  692. dibSection = CreateDIBSection(
  693. NULL,
  694. (BITMAPINFO*) &header,
  695. DIB_RGB_COLORS,
  696. &srcBmpData,
  697. NULL,
  698. 0);
  699. if (!dibSection)
  700. __leave;
  701. //
  702. // Blt from the bitmap file to the DIB section
  703. //
  704. HBITMAP hbmp;
  705. hdc = CreateCompatibleDC(NULL);
  706. hbmp = (HBITMAP) SelectObject(hdc, dibSection);
  707. StretchDIBits(
  708. hdc,
  709. 0, 0, srcSize.cx, srcSize.cy,
  710. 0, 0, srcSize.cx, srcSize.cy,
  711. bmpdata,
  712. bmpinfo,
  713. DIB_RGB_COLORS,
  714. SRCCOPY);
  715. SelectObject(hdc, hbmp);
  716. success = TRUE;
  717. }
  718. __except(EXCEPTION_EXECUTE_HANDLER)
  719. {
  720. // AV while reading bitmap file
  721. }
  722. if (hdc)
  723. DeleteDC(hdc);
  724. if (fileview)
  725. UnmapViewOfFile(fileview);
  726. if (!success)
  727. Error("Failed to read source bitmap\n");
  728. }
  729. //
  730. // Main program entrypoint
  731. //
  732. INT _cdecl
  733. main(
  734. INT argc,
  735. CHAR **argv
  736. )
  737. {
  738. programName = *argv++;
  739. argc--;
  740. appInstance = GetModuleHandle(NULL);
  741. // Load source bitmap file
  742. if (argc != 1)
  743. Error("usage: %s bitmapfile\n", programName);
  744. LoadBitmapFile(*argv);
  745. // Initialize mesh configuration
  746. CreateMesh();
  747. // Create the main application window
  748. CreateMainWindow();
  749. // Main message loop
  750. MSG msg;
  751. while (GetMessage(&msg, NULL, 0, 0))
  752. {
  753. TranslateMessage(&msg);
  754. DispatchMessage(&msg);
  755. }
  756. return msg.wParam;
  757. }