/win32_cairo_doublebuffer.d

http://github.com/AndrejMitrovic/cairoDSamples · D · 331 lines · 223 code · 60 blank · 48 comment · 9 complexity · 89869bf7d7e30653a519a1df2ee29cc5 MD5 · raw file

  1. module win32_cairo_doublebuffer;
  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 usage of a double-buffer using Cairo and win32.
  10. + Also shows how to avoid re-blitting an area by simply using the
  11. + ps.rcPaint bounding rectangle field, and removing redrawing of
  12. + the entire window when it is resized.
  13. +
  14. + For more info on double-buffering and avoiding screen flicker, see:
  15. + http://wiki.osdev.org/Double_Buffering
  16. + http://www.catch22.net/tuts/flicker
  17. +/
  18. import core.runtime;
  19. import std.process;
  20. import std.stdio;
  21. import std.utf;
  22. pragma(lib, "gdi32.lib");
  23. import win32.windef;
  24. import win32.winuser;
  25. import win32.wingdi;
  26. string appName = "CairoWindow";
  27. string description = "A simple win32 window with Cairo drawing";
  28. HINSTANCE hinst;
  29. import cairo.cairo;
  30. import cairo.win32;
  31. alias cairo.cairo.RGB RGB; // conflicts with win32.wingdi.RGB
  32. extern (Windows)
  33. int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  34. {
  35. int result;
  36. void exceptionHandler(Throwable e) { throw e; }
  37. try
  38. {
  39. Runtime.initialize(&exceptionHandler);
  40. result = myWinMain(hInstance, hPrevInstance, lpCmdLine, iCmdShow);
  41. Runtime.terminate(&exceptionHandler);
  42. }
  43. catch (Throwable o)
  44. {
  45. MessageBox(null, o.toString().toUTF16z, "Error", MB_OK | MB_ICONEXCLAMATION);
  46. result = 0;
  47. }
  48. return result;
  49. }
  50. int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  51. {
  52. hinst = hInstance;
  53. HACCEL hAccel;
  54. HWND hwnd;
  55. MSG msg;
  56. WNDCLASS wndclass;
  57. // commented out so we do not redraw the entire screen on resize
  58. //~ wndclass.style = CS_HREDRAW | CS_VREDRAW;
  59. wndclass.style = WS_CLIPCHILDREN;
  60. wndclass.lpfnWndProc = &WndProc;
  61. wndclass.cbClsExtra = 0;
  62. wndclass.cbWndExtra = 0;
  63. wndclass.hInstance = hInstance;
  64. wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  65. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  66. wndclass.hbrBackground = null;
  67. wndclass.lpszMenuName = appName.toUTF16z;
  68. wndclass.lpszClassName = appName.toUTF16z;
  69. if (!RegisterClass(&wndclass))
  70. {
  71. MessageBox(NULL, "This program requires Windows NT!", appName.toUTF16z, MB_ICONERROR);
  72. return 0;
  73. }
  74. hwnd = CreateWindow(appName.toUTF16z, // window class name
  75. description.toUTF16z, // window caption
  76. WS_OVERLAPPEDWINDOW, // window style
  77. CW_USEDEFAULT, // initial x position
  78. CW_USEDEFAULT, // initial y position
  79. CW_USEDEFAULT, // initial x size
  80. CW_USEDEFAULT, // initial y size
  81. NULL, // parent window handle
  82. NULL, // window menu handle
  83. hInstance, // program instance handle
  84. NULL); // creation parameters
  85. ShowWindow(hwnd, iCmdShow);
  86. UpdateWindow(hwnd);
  87. while (GetMessage(&msg, NULL, 0, 0))
  88. {
  89. TranslateMessage(&msg);
  90. DispatchMessage(&msg);
  91. }
  92. return msg.wParam;
  93. }
  94. void roundedRectangle(Context ctx, int x, int y, int w, int h, int radius_x = 5, int radius_y = 5)
  95. {
  96. enum ARC_TO_BEZIER = 0.55228475;
  97. if (radius_x > w - radius_x)
  98. radius_x = w / 2;
  99. if (radius_y > h - radius_y)
  100. radius_y = h / 2;
  101. // approximate (quite close) the arc using a bezier curve
  102. auto c1 = ARC_TO_BEZIER * radius_x;
  103. auto c2 = ARC_TO_BEZIER * radius_y;
  104. ctx.newPath();
  105. ctx.moveTo(x + radius_x, y);
  106. ctx.relLineTo(w - 2 * radius_x, 0.0);
  107. ctx.relCurveTo(c1, 0.0, radius_x, c2, radius_x, radius_y);
  108. ctx.relLineTo(0, h - 2 * radius_y);
  109. ctx.relCurveTo(0.0, c2, c1 - radius_x, radius_y, -radius_x, radius_y);
  110. ctx.relLineTo(-w + 2 * radius_x, 0);
  111. ctx.relCurveTo(-c1, 0, -radius_x, -c2, -radius_x, -radius_y);
  112. ctx.relLineTo(0, -h + 2 * radius_y);
  113. ctx.relCurveTo(0.0, -c2, radius_x - c1, -radius_y, radius_x, -radius_y);
  114. ctx.closePath();
  115. }
  116. extern (Windows)
  117. LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  118. {
  119. switch (message)
  120. {
  121. case WM_CREATE:
  122. {
  123. window = new Window(hwnd);
  124. return 0;
  125. }
  126. default:
  127. }
  128. if (window)
  129. return window.process(hwnd, message, wParam, lParam);
  130. else
  131. return DefWindowProc(hwnd, message, wParam, lParam);
  132. }
  133. Window window;
  134. struct PaintBuffer
  135. {
  136. int width, height;
  137. this(HDC localHdc, int cxClient, int cyClient)
  138. {
  139. width = cxClient;
  140. height = cyClient;
  141. hBuffer = CreateCompatibleDC(localHdc);
  142. hBitmap = CreateCompatibleBitmap(localHdc, cxClient, cyClient);
  143. hOldBitmap = SelectObject(hBuffer, hBitmap);
  144. surf = new Win32Surface(hBuffer);
  145. ctx = Context(surf);
  146. initialized = true;
  147. }
  148. ~this()
  149. {
  150. if (initialized) // struct dtors are still buggy sometimes
  151. {
  152. ctx.dispose();
  153. surf.finish();
  154. surf.dispose();
  155. SelectObject(hBuffer, hOldBitmap);
  156. DeleteObject(hBitmap);
  157. DeleteDC(hBuffer);
  158. initialized = false;
  159. }
  160. }
  161. bool initialized;
  162. HDC hBuffer;
  163. HBITMAP hBitmap;
  164. HBITMAP hOldBitmap;
  165. Context ctx;
  166. Surface surf;
  167. }
  168. class Window
  169. {
  170. int width, height;
  171. HWND hwnd;
  172. PAINTSTRUCT ps;
  173. PaintBuffer paintBuffer;
  174. bool needsRedraw;
  175. this(HWND hwnd)
  176. {
  177. this.hwnd = hwnd;
  178. auto hDesk = GetDesktopWindow();
  179. RECT rc;
  180. GetClientRect(hDesk, &rc);
  181. auto localHdc = GetDC(hwnd);
  182. paintBuffer = PaintBuffer(localHdc, rc.right, rc.bottom);
  183. needsRedraw = true;
  184. }
  185. LRESULT process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  186. {
  187. switch (message)
  188. {
  189. case WM_DESTROY:
  190. PostQuitMessage(0);
  191. return 0;
  192. case WM_PAINT:
  193. OnPaint(hwnd, message, wParam, lParam);
  194. return 0;
  195. case WM_ERASEBKGND:
  196. return 1;
  197. case WM_SIZE:
  198. {
  199. width = LOWORD(lParam);
  200. height = HIWORD(lParam);
  201. return 0;
  202. }
  203. default:
  204. }
  205. return DefWindowProc(hwnd, message, wParam, lParam);
  206. }
  207. void OnPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  208. {
  209. static int blitCount;
  210. auto hdc = BeginPaint(hwnd, &ps);
  211. auto ctx = paintBuffer.ctx;
  212. auto hBuffer = paintBuffer.hBuffer;
  213. auto boundRect = ps.rcPaint;
  214. with (boundRect)
  215. {
  216. system("cls");
  217. writefln("left: %.3s, top: %.3s, bottom: %.3s, right: %.3s", left, top, bottom, right);
  218. }
  219. if (needsRedraw) // cairo needs to redraw
  220. {
  221. draw(ctx);
  222. needsRedraw = false;
  223. }
  224. writefln("blit: %.3s", ++blitCount);
  225. with (boundRect) // blit only required areas
  226. {
  227. BitBlt(hdc, left, top, right, bottom, hBuffer, left, top, SRCCOPY);
  228. }
  229. EndPaint(hwnd, &ps);
  230. }
  231. void draw(Context ctx)
  232. {
  233. ctx.setSourceRGB(1, 1, 1);
  234. ctx.paint();
  235. ctx.rectangle(0, 0, 120, 90);
  236. ctx.setSourceRGBA(0.7, 0, 0, 0.8);
  237. ctx.fill();
  238. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_CLEAR);
  239. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_SOURCE);
  240. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_OVER);
  241. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_IN);
  242. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_OUT);
  243. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_ATOP);
  244. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST);
  245. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST_OVER);
  246. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST_IN);
  247. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST_OUT);
  248. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST_ATOP);
  249. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST_XOR);
  250. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST_ADD);
  251. //~ ctx.setOperator(Operator.CAIRO_OPERATOR_DEST_SATURATE);
  252. //~ // blue
  253. ctx.rectangle(40, 30, 120, 90);
  254. ctx.setSourceRGBA(0, 0, 0.9, 0.4);
  255. ctx.fill();
  256. //~ auto color = RGB(0.9, 0.9, 0.9);
  257. //~ ctx.setSourceRGB(color);
  258. //~ ctx.translate(100, 100);
  259. //~ ctx.rectangle(0, 0, 200, 200);
  260. //~ ctx.strokePreserve();
  261. //~ ctx.clip();
  262. //~ ctx.rectangle(0, 50, 100, 100);
  263. //~ ctx.fill();
  264. //~ ctx.setSourceRGB(color);
  265. //~ roundedRectangle(ctx, 50, 50, 250, 250, 10, 10);
  266. //~ ctx.fillPreserve();
  267. //~ color = RGB(0.9, 0.9, 0.9);
  268. //~ ctx.setSourceRGB(color);
  269. //~ ctx.stroke();
  270. }
  271. }