PageRenderTime 79ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/win32_clipped_draw.d

http://github.com/AndrejMitrovic/cairoDSamples
D | 557 lines | 413 code | 105 blank | 39 comment | 10 complexity | a356039cf0645deddda9715e5f3c26f9 MD5 | raw file
  1. module win32_clipped_draw;
  2. /+
  3. + Copyright Andrej Mitrovic 2011.
  4. + Distributed under the Boost Software License, Version 1.0.
  5. + (See accompanying file LICENSE_1_0.txt or copy at
  6. + http://www.boost.org/LICENSE_1_0.txt)
  7. +/
  8. /+
  9. + Demonstrates the use of WS_CLIPCHILDREN when calling CreateWindow().
  10. + This clips the drawing of a parent window with any child windows,
  11. + therefore it won't draw over the children's areas, avoiding flicker.
  12. +
  13. + I'm also using the ps.rcPaint from the BeginPaint call to limit
  14. + blitting to only the areas that need to be updated. I'm also not
  15. + re-drawing (with cairo) the areas of a widget that have already
  16. + been drawn.
  17. +
  18. + These techniques give us good drawing and blitting performance.
  19. + We could also dynamically create the backbuffer for the main
  20. + window (atm. it creates a memory buffer the size of the screen).
  21. +/
  22. import core.memory;
  23. import core.runtime;
  24. import core.thread;
  25. import core.stdc.config;
  26. import std.algorithm;
  27. import std.array;
  28. import std.conv;
  29. import std.exception;
  30. import std.functional;
  31. import std.math;
  32. import std.random;
  33. import std.range;
  34. import std.stdio;
  35. import std.string;
  36. import std.traits;
  37. import std.utf;
  38. pragma(lib, "gdi32.lib");
  39. import win32.windef;
  40. import win32.winuser;
  41. import win32.wingdi;
  42. alias std.algorithm.min min; // conflict resolution
  43. alias std.algorithm.max max; // conflict resolution
  44. import cairo.cairo;
  45. import cairo.win32;
  46. alias cairo.cairo.RGB RGB; // conflict resolution
  47. struct StateContext
  48. {
  49. Context ctx;
  50. this(Context ctx)
  51. {
  52. this.ctx = ctx;
  53. ctx.save();
  54. }
  55. ~this()
  56. {
  57. ctx.restore();
  58. }
  59. alias ctx this;
  60. }
  61. class PaintBuffer
  62. {
  63. this(HDC localHdc, int cxClient, int cyClient)
  64. {
  65. hdc = localHdc;
  66. width = cxClient;
  67. height = cyClient;
  68. hBuffer = CreateCompatibleDC(localHdc);
  69. hBitmap = CreateCompatibleBitmap(localHdc, cxClient, cyClient);
  70. hOldBitmap = SelectObject(hBuffer, hBitmap);
  71. surf = new Win32Surface(hBuffer);
  72. ctx = Context(surf);
  73. initialized = true;
  74. }
  75. ~this()
  76. {
  77. if (initialized)
  78. {
  79. clear();
  80. }
  81. }
  82. void clear()
  83. {
  84. ctx.dispose();
  85. surf.finish();
  86. surf.dispose();
  87. SelectObject(hBuffer, hOldBitmap);
  88. DeleteObject(hBitmap);
  89. DeleteDC(hBuffer);
  90. initialized = false;
  91. }
  92. HDC hdc;
  93. bool initialized;
  94. int width, height;
  95. HDC hBuffer;
  96. HBITMAP hBitmap;
  97. HBITMAP hOldBitmap;
  98. Context ctx;
  99. Surface surf;
  100. }
  101. abstract class Widget
  102. {
  103. Widget parent;
  104. PAINTSTRUCT ps;
  105. PaintBuffer mainPaintBuff;
  106. PaintBuffer paintBuffer;
  107. HWND hwnd;
  108. int width, height;
  109. int xOffset, yOffset;
  110. bool needsRedraw = true;
  111. this(HWND hwnd, int width, int height)
  112. {
  113. this.hwnd = hwnd;
  114. this.width = width;
  115. this.height = height;
  116. //~ SetTimer(hwnd, 100, 1, NULL);
  117. }
  118. @property Size!int size()
  119. {
  120. return Size!int(width, height);
  121. }
  122. abstract LRESULT process(UINT message, WPARAM wParam, LPARAM lParam)
  123. {
  124. switch (message)
  125. {
  126. case WM_ERASEBKGND:
  127. {
  128. return 1;
  129. }
  130. case WM_PAINT:
  131. {
  132. OnPaint(hwnd, message, wParam, lParam);
  133. return 0;
  134. }
  135. case WM_SIZE:
  136. {
  137. width = LOWORD(lParam);
  138. height = HIWORD(lParam);
  139. auto localHdc = GetDC(hwnd);
  140. if (paintBuffer !is null)
  141. {
  142. paintBuffer.clear();
  143. }
  144. paintBuffer = new PaintBuffer(localHdc, width, height);
  145. ReleaseDC(hwnd, localHdc);
  146. needsRedraw = true;
  147. InvalidateRect(hwnd, null, true);
  148. return 0;
  149. }
  150. case WM_TIMER:
  151. {
  152. InvalidateRect(hwnd, null, true);
  153. return 0;
  154. }
  155. case WM_MOVE:
  156. {
  157. xOffset = LOWORD(lParam);
  158. yOffset = HIWORD(lParam);
  159. return 0;
  160. }
  161. default:
  162. }
  163. return DefWindowProc(hwnd, message, wParam, lParam);
  164. }
  165. abstract void OnPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  166. abstract void draw(StateContext ctx);
  167. }
  168. class TestWidget2 : Widget
  169. {
  170. this(HWND hwnd, int width, int height)
  171. {
  172. super(hwnd, width, height);
  173. }
  174. override LRESULT process(UINT message, WPARAM wParam, LPARAM lParam)
  175. {
  176. return super.process(message, wParam, lParam);
  177. }
  178. override void OnPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  179. {
  180. auto ctx = paintBuffer.ctx;
  181. auto hBuffer = paintBuffer.hBuffer;
  182. auto hdc = BeginPaint(hwnd, &ps);
  183. auto boundRect = ps.rcPaint;
  184. if (needsRedraw)
  185. {
  186. //~ writeln("drawing");
  187. draw(StateContext(ctx));
  188. needsRedraw = false;
  189. }
  190. with (boundRect)
  191. {
  192. //~ writeln("blitting");
  193. BitBlt(hdc, left, top, right - left, bottom - top, paintBuffer.hBuffer, left, top, SRCCOPY);
  194. }
  195. EndPaint(hwnd, &ps);
  196. }
  197. override void draw(StateContext ctx)
  198. {
  199. ctx.setSourceRGB(1, 1, 1);
  200. ctx.paint();
  201. ctx.scale(width, height);
  202. ctx.moveTo(0, 0);
  203. ctx.rectangle(0, 0, 1, 1);
  204. ctx.setSourceRGBA(1, 1, 1, 0);
  205. ctx.setOperator(Operator.CAIRO_OPERATOR_CLEAR);
  206. ctx.fill();
  207. ctx.setSourceRGB(0, 0, 0);
  208. ctx.setOperator(Operator.CAIRO_OPERATOR_OVER);
  209. auto linpat = new LinearGradient(0, 0, 1, 1);
  210. linpat.addColorStopRGB(0, RGB(0, 0.3, 0.8));
  211. linpat.addColorStopRGB(1, RGB(0, 0.8, 0.3));
  212. auto radpat = new RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.75);
  213. radpat.addColorStopRGBA(0, RGBA(0, 0, 0, 1));
  214. radpat.addColorStopRGBA(0.5, RGBA(0, 0, 0, 0));
  215. ctx.setSource(linpat);
  216. ctx.mask(radpat);
  217. }
  218. }
  219. class TestWidget : Widget
  220. {
  221. RGB backColor;
  222. this(HWND hwnd, int width, int height)
  223. {
  224. super(hwnd, width, height);
  225. this.backColor = RGB(1, 0, 0);
  226. auto localHdc = GetDC(hwnd);
  227. auto hWindow = CreateWindow(WidgetClass.toUTF16z, NULL,
  228. WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN, // WS_CLIPCHILDREN is necessary
  229. 0, 0, 0, 0,
  230. hwnd, cast(HANDLE)1, // child ID
  231. cast(HINSTANCE)GetWindowLongPtr(hwnd, GWL_HINSTANCE), // hInstance
  232. NULL);
  233. auto widget = new TestWidget2(hWindow, width / 2, width / 2);
  234. WidgetHandles[hWindow] = widget;
  235. auto size = widget.size;
  236. MoveWindow(hWindow, size.width / 2, size.height / 2, size.width, size.height, true);
  237. }
  238. override LRESULT process(UINT message, WPARAM wParam, LPARAM lParam)
  239. {
  240. return super.process(message, wParam, lParam);
  241. }
  242. override void OnPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  243. {
  244. auto ctx = paintBuffer.ctx;
  245. auto hBuffer = paintBuffer.hBuffer;
  246. auto hdc = BeginPaint(hwnd, &ps);
  247. auto boundRect = ps.rcPaint;
  248. if (needsRedraw)
  249. {
  250. //~ writeln("drawing");
  251. draw(StateContext(ctx));
  252. needsRedraw = false;
  253. }
  254. with (boundRect)
  255. {
  256. //~ writeln("blitting");
  257. BitBlt(hdc, left, top, right - left, bottom - top, paintBuffer.hBuffer, left, top, SRCCOPY);
  258. }
  259. EndPaint(hwnd, &ps);
  260. }
  261. override void draw(StateContext ctx)
  262. {
  263. ctx.save();
  264. ctx.scale(width, height);
  265. ctx.moveTo(0, 0);
  266. ctx.rectangle(0, 0, 1, 1);
  267. ctx.setSourceRGBA(1, 1, 1, 0);
  268. ctx.setOperator(Operator.CAIRO_OPERATOR_CLEAR);
  269. ctx.fill();
  270. ctx.setSourceRGB(0, 0, 0);
  271. ctx.setOperator(Operator.CAIRO_OPERATOR_OVER);
  272. auto linpat = new LinearGradient(0, 0, 1, 1);
  273. linpat.addColorStopRGB(0, RGB(0, 0.3, 0.8));
  274. linpat.addColorStopRGB(1, RGB(0, 0.8, 0.3));
  275. auto radpat = new RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.75);
  276. radpat.addColorStopRGBA(0, RGBA(0, 0, 0, 1));
  277. radpat.addColorStopRGBA(0.5, RGBA(0, 0, 0, 0));
  278. ctx.setSource(linpat);
  279. ctx.mask(radpat);
  280. ctx.moveTo(0.1, 0.5);
  281. ctx.restore();
  282. ctx.setSourceRGB(1, 1, 1);
  283. ctx.selectFontFace("Tahoma", FontSlant.CAIRO_FONT_SLANT_NORMAL, FontWeight.CAIRO_FONT_WEIGHT_NORMAL);
  284. ctx.setFontSize(20);
  285. ctx.showText("weeeeeeeeeeeeeeeeeeeeeeeeeee");
  286. }
  287. }
  288. /* A place to hold Widget objects. Since each window has a unique HWND,
  289. * we can use this hash type to store references to Widgets and call
  290. * their window processing methods.
  291. */
  292. Widget[HWND] WidgetHandles;
  293. /*
  294. * All Widget windows have this window procedure registered via RegisterClass(),
  295. * we use it to dispatch to the appropriate Widget window processing method.
  296. *
  297. * A similar technique is used in the DFL and DGUI libraries for all of its
  298. * windows and widgets.
  299. */
  300. extern (Windows)
  301. LRESULT winDispatch(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  302. {
  303. auto widget = hwnd in WidgetHandles;
  304. if (widget !is null)
  305. {
  306. return widget.process(message, wParam, lParam);
  307. }
  308. return DefWindowProc(hwnd, message, wParam, lParam);
  309. }
  310. extern (Windows)
  311. LRESULT mainWinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  312. {
  313. static PaintBuffer paintBuffer;
  314. static int width, height;
  315. static int TimerID = 16;
  316. static HMENU widgetID = cast(HMENU)0; // todo: each widget has its own HMENU ID
  317. void draw(StateContext ctx)
  318. {
  319. ctx.setSourceRGB(0.3, 0.3, 0.3);
  320. ctx.paint();
  321. }
  322. switch (message)
  323. {
  324. case WM_CREATE:
  325. {
  326. auto hDesk = GetDesktopWindow();
  327. RECT rc;
  328. GetClientRect(hDesk, &rc);
  329. auto localHdc = GetDC(hwnd);
  330. paintBuffer = new PaintBuffer(localHdc, rc.right, rc.bottom);
  331. auto hWindow = CreateWindow(WidgetClass.toUTF16z, NULL,
  332. WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN, // WS_CLIPCHILDREN is necessary
  333. 0, 0, 0, 0,
  334. hwnd, widgetID, // child ID
  335. cast(HINSTANCE)GetWindowLongPtr(hwnd, GWL_HINSTANCE), // hInstance
  336. NULL);
  337. auto widget = new TestWidget(hWindow, 400, 400);
  338. WidgetHandles[hWindow] = widget;
  339. auto size = widget.size;
  340. MoveWindow(hWindow, size.width / 3, size.width / 3, size.width, size.height, true);
  341. //~ SetTimer(hwnd, TimerID, 1, NULL);
  342. return 0;
  343. }
  344. case WM_LBUTTONDOWN:
  345. {
  346. SetFocus(hwnd);
  347. return 0;
  348. }
  349. case WM_SIZE:
  350. {
  351. width = LOWORD(lParam);
  352. height = HIWORD(lParam);
  353. return 0;
  354. }
  355. case WM_PAINT:
  356. {
  357. auto ctx = paintBuffer.ctx;
  358. auto hBuffer = paintBuffer.hBuffer;
  359. PAINTSTRUCT ps;
  360. auto hdc = BeginPaint(hwnd, &ps);
  361. auto boundRect = ps.rcPaint;
  362. draw(StateContext(paintBuffer.ctx));
  363. with (boundRect)
  364. {
  365. BitBlt(hdc, left, top, right - left, bottom - top, paintBuffer.hBuffer, left, top, SRCCOPY);
  366. }
  367. EndPaint(hwnd, &ps);
  368. return 0;
  369. }
  370. case WM_TIMER:
  371. {
  372. InvalidateRect(hwnd, null, true);
  373. return 0;
  374. }
  375. case WM_MOUSEWHEEL:
  376. {
  377. return 0;
  378. }
  379. case WM_DESTROY:
  380. {
  381. PostQuitMessage(0);
  382. return 0;
  383. }
  384. default:
  385. }
  386. return DefWindowProc(hwnd, message, wParam, lParam);
  387. }
  388. string WidgetClass = "WidgetClass";
  389. int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  390. {
  391. string appName = "layered drawing";
  392. HWND hwnd;
  393. MSG msg;
  394. WNDCLASS wndclass;
  395. /* One class for the main window */
  396. wndclass.lpfnWndProc = &mainWinProc;
  397. wndclass.cbClsExtra = 0;
  398. wndclass.cbWndExtra = 0;
  399. wndclass.hInstance = hInstance;
  400. wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  401. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  402. wndclass.hbrBackground = null;
  403. wndclass.lpszMenuName = NULL;
  404. wndclass.lpszClassName = appName.toUTF16z;
  405. if (!RegisterClass(&wndclass))
  406. {
  407. MessageBox(NULL, "This program requires Windows NT!", appName.toUTF16z, MB_ICONERROR);
  408. return 0;
  409. }
  410. /* Separate window class for Widgets. */
  411. wndclass.hbrBackground = null;
  412. wndclass.lpfnWndProc = &winDispatch;
  413. wndclass.cbWndExtra = 0;
  414. wndclass.hIcon = NULL;
  415. wndclass.lpszClassName = WidgetClass.toUTF16z;
  416. if (!RegisterClass(&wndclass))
  417. {
  418. MessageBox(NULL, "This program requires Windows NT!", appName.toUTF16z, MB_ICONERROR);
  419. return 0;
  420. }
  421. hwnd = CreateWindow(appName.toUTF16z, "layered drawing",
  422. WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, // WS_CLIPCHILDREN is necessary
  423. CW_USEDEFAULT, CW_USEDEFAULT,
  424. CW_USEDEFAULT, CW_USEDEFAULT,
  425. NULL, NULL, hInstance, NULL);
  426. ShowWindow(hwnd, iCmdShow);
  427. UpdateWindow(hwnd);
  428. while (GetMessage(&msg, NULL, 0, 0))
  429. {
  430. TranslateMessage(&msg);
  431. DispatchMessage(&msg);
  432. }
  433. return msg.wParam;
  434. }
  435. extern (Windows)
  436. int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  437. {
  438. int result;
  439. void exceptionHandler(Throwable e) { throw e; }
  440. try
  441. {
  442. Runtime.initialize(&exceptionHandler);
  443. myWinMain(hInstance, hPrevInstance, lpCmdLine, iCmdShow);
  444. Runtime.terminate(&exceptionHandler);
  445. }
  446. catch (Throwable o)
  447. {
  448. MessageBox(null, o.toString().toUTF16z, "Error", MB_OK | MB_ICONEXCLAMATION);
  449. result = -1;
  450. }
  451. return result;
  452. }