/win32_cairo_operator.d

http://github.com/AndrejMitrovic/cairoDSamples · D · 381 lines · 265 code · 60 blank · 56 comment · 11 complexity · 4f6cdb387ed88b8139c37aa92e480892 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. import std.utf : toUTFz;
  9. alias toUTFz!(const(wchar)*, string) toUTF16z;
  10. alias toUTFz!(const(wchar)*, wstring) toUTF16z;
  11. alias toUTFz!(const(wchar)*, dstring) toUTF16z;
  12. /+
  13. + Demonstrates the usage of Cairo compositing operators and alpha-blitting.
  14. +
  15. + Note: Other samples in this directory do not use alpha-blending, and should
  16. + probably be rewritten. Sorry for that! :)
  17. +
  18. + Notes:
  19. + You have to use an RGB32 surface type, otherwise you won't be able
  20. + to use numerous alpha-blending operators (you will get a runtime exception).
  21. +
  22. + I'm using CairoD's Win32Surface ctor that can create a surface type
  23. + based on the format enum supplied. Alternatively I could have manually
  24. + created a DIBSection and construct a Win32Surface with that.
  25. +
  26. + I'm using AlphaBlend (symbol name is actually 'GdiAlphaBlend' in Gdi32.lib),
  27. + if you get undefined symbol errors it could mean the Gdi32.lib import lib
  28. + distributed with DMD/GDC is outdated. You can create a new OMF-compatible
  29. + one by grabbing the Windows SDK, downloading coffimplib
  30. + (ftp://ftp.digitalmars.com/coffimplib.zip), and browsing to
  31. + where Gdi32.lib is located, e.g.:
  32. +
  33. + C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib
  34. +
  35. + and calling:
  36. + coffimplib Gdi32_coff.lib gdi32.lib
  37. +
  38. + Then copy 'Gdi32_coff.lib' to DMD\dmd2\windows\lib\, delete the old 'gdi32.lib',
  39. + and rename 'Gdi32_coff.lib' to 'gdi32.lib'.
  40. +
  41. + I use 2 paint buffers, one is the foreground that only paints to certain
  42. + regions and has an alpha, the other acts as a background with its entire
  43. + surface painted white with alpha 1.0 (max).
  44. +
  45. + I use AlphaBlit to blit the foreground to the background. AlphaBlit is set
  46. + to blit by using per-pixel alpha values (this is configurable to other settings).
  47. +
  48. + The reason I'm using 2 paint buffers and not just 1 foreground buffer and
  49. + a white pre-painted window device context is because the latter usually introduces
  50. + graphical glitches. For example, painting the window white directly via a
  51. + device context (and e.g. the GDI FillRect function) and then alpha-blitting the
  52. + foreground results in 2 refresh events. This would introduce flicker effects,
  53. + so it's safer to keep a separate background buffer to blend with when drawing,
  54. + and then blit to the screen as necessary.
  55. +/
  56. import core.runtime;
  57. import std.process;
  58. import std.stdio;
  59. //~ import std.utf;
  60. pragma(lib, "gdi32.lib");
  61. import win32.windef;
  62. import win32.winuser;
  63. import win32.wingdi;
  64. string appName = "CairoWindow";
  65. string description = "A simple win32 window with Cairo drawing";
  66. HINSTANCE hinst;
  67. import cairo.cairo;
  68. import cairo.win32;
  69. alias cairo.cairo.RGB RGB; // conflicts with win32.wingdi.RGB
  70. struct AlphaBlendType
  71. {
  72. static normal = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA);
  73. }
  74. extern(Windows) BOOL GdiAlphaBlend(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);
  75. void AlphaBlit(HDC dstHdc, HDC srcHdc, int width, int height, BLENDFUNCTION blendType = AlphaBlendType.normal)
  76. {
  77. auto result = GdiAlphaBlend(dstHdc, 0, 0, width, height,
  78. srcHdc, 0, 0, width, height, blendType);
  79. if (result == 0)
  80. {
  81. //~ auto error = GetLastError();
  82. //~ msgbox(error);
  83. }
  84. }
  85. extern (Windows)
  86. int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  87. {
  88. int result;
  89. void exceptionHandler(Throwable e) { throw e; }
  90. try
  91. {
  92. Runtime.initialize(&exceptionHandler);
  93. result = myWinMain(hInstance, hPrevInstance, lpCmdLine, iCmdShow);
  94. Runtime.terminate(&exceptionHandler);
  95. }
  96. catch (Throwable o)
  97. {
  98. MessageBox(null, o.toString().toUTF16z, "Error", MB_OK | MB_ICONEXCLAMATION);
  99. result = 0;
  100. }
  101. return result;
  102. }
  103. int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  104. {
  105. hinst = hInstance;
  106. HACCEL hAccel;
  107. HWND hwnd;
  108. MSG msg;
  109. WNDCLASS wndclass;
  110. // commented out so we do not redraw the entire screen on resize
  111. //~ wndclass.style = CS_HREDRAW | CS_VREDRAW;
  112. wndclass.style = WS_CLIPCHILDREN;
  113. wndclass.lpfnWndProc = &WndProc;
  114. wndclass.cbClsExtra = 0;
  115. wndclass.cbWndExtra = 0;
  116. wndclass.hInstance = hInstance;
  117. wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  118. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  119. wndclass.hbrBackground = null;
  120. wndclass.lpszMenuName = appName.toUTF16z;
  121. wndclass.lpszClassName = appName.toUTF16z;
  122. if (!RegisterClass(&wndclass))
  123. {
  124. MessageBox(NULL, "This program requires Windows NT!", appName.toUTF16z, MB_ICONERROR);
  125. return 0;
  126. }
  127. hwnd = CreateWindow(appName.toUTF16z, // window class name
  128. description.toUTF16z, // window caption
  129. WS_OVERLAPPEDWINDOW, // window style
  130. (1680 - 900) / 2, // initial x position
  131. (1050 - 700) / 2, // initial y position
  132. 1100, // initial x size
  133. 800, // initial y size
  134. NULL, // parent window handle
  135. NULL, // window menu handle
  136. hInstance, // program instance handle
  137. NULL); // creation parameters
  138. ShowWindow(hwnd, iCmdShow);
  139. UpdateWindow(hwnd);
  140. while (GetMessage(&msg, NULL, 0, 0))
  141. {
  142. TranslateMessage(&msg);
  143. DispatchMessage(&msg);
  144. }
  145. return msg.wParam;
  146. }
  147. extern (Windows)
  148. LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  149. {
  150. switch (message)
  151. {
  152. case WM_CREATE:
  153. {
  154. window = new Window(hwnd);
  155. return 0;
  156. }
  157. default:
  158. }
  159. if (window)
  160. return window.process(hwnd, message, wParam, lParam);
  161. else
  162. return DefWindowProc(hwnd, message, wParam, lParam);
  163. }
  164. Window window;
  165. struct PaintBuffer
  166. {
  167. int width, height;
  168. this(HDC localHdc, int cxClient, int cyClient)
  169. {
  170. surf = new Win32Surface(Format.CAIRO_FORMAT_ARGB32, cxClient, cyClient);
  171. ctx = Context(surf);
  172. initialized = true;
  173. }
  174. ~this()
  175. {
  176. if (initialized) // struct dtors are still buggy sometimes
  177. {
  178. ctx.dispose();
  179. surf.finish();
  180. surf.dispose();
  181. initialized = false;
  182. }
  183. }
  184. bool initialized;
  185. Context ctx;
  186. Win32Surface surf;
  187. }
  188. class Window
  189. {
  190. int width, height;
  191. HWND hwnd;
  192. PAINTSTRUCT ps;
  193. PaintBuffer paintBuffer;
  194. PaintBuffer paintBufferNew;
  195. bool needsRedraw;
  196. this(HWND hwnd)
  197. {
  198. this.hwnd = hwnd;
  199. auto hDesk = GetDesktopWindow();
  200. RECT rc;
  201. GetClientRect(hDesk, &rc);
  202. auto localHdc = GetDC(hwnd);
  203. paintBuffer = PaintBuffer(localHdc, rc.right, rc.bottom);
  204. paintBufferNew = PaintBuffer(localHdc, rc.right, rc.bottom);
  205. needsRedraw = true;
  206. }
  207. LRESULT process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  208. {
  209. switch (message)
  210. {
  211. case WM_DESTROY:
  212. PostQuitMessage(0);
  213. return 0;
  214. case WM_PAINT:
  215. OnPaint(hwnd, message, wParam, lParam);
  216. return 0;
  217. case WM_ERASEBKGND:
  218. return 1;
  219. case WM_SIZE:
  220. {
  221. width = LOWORD(lParam);
  222. height = HIWORD(lParam);
  223. return 0;
  224. }
  225. default:
  226. }
  227. return DefWindowProc(hwnd, message, wParam, lParam);
  228. }
  229. void OnPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  230. {
  231. static int blitCount;
  232. auto hdc = BeginPaint(hwnd, &ps);
  233. auto ctx = paintBuffer.ctx;
  234. auto hBuffer = paintBuffer.surf.getDC;
  235. auto boundRect = ps.rcPaint;
  236. if (needsRedraw) // cairo needs to redraw
  237. {
  238. draw(ctx);
  239. with (boundRect)
  240. {
  241. paintBufferNew.ctx.setSourceRGBA(1, 1, 1, 1);
  242. paintBufferNew.ctx.paint();
  243. AlphaBlit(paintBufferNew.surf.getDC(), hBuffer, right - left, bottom - top);
  244. }
  245. needsRedraw = false;
  246. }
  247. with (boundRect) // blit only required areas
  248. {
  249. BitBlt(hdc, left, top, right, bottom, paintBufferNew.surf.getDC(), left, top, SRCCOPY);
  250. }
  251. EndPaint(hwnd, &ps);
  252. }
  253. void draw(Context ctx)
  254. {
  255. import std.traits;
  256. static size_t yIndex;
  257. static size_t xIndex;
  258. auto ops =
  259. [
  260. Operator.CAIRO_OPERATOR_CLEAR,
  261. Operator.CAIRO_OPERATOR_SOURCE,
  262. Operator.CAIRO_OPERATOR_OVER,
  263. Operator.CAIRO_OPERATOR_IN,
  264. Operator.CAIRO_OPERATOR_OUT,
  265. Operator.CAIRO_OPERATOR_ATOP,
  266. Operator.CAIRO_OPERATOR_DEST,
  267. Operator.CAIRO_OPERATOR_DEST_OVER,
  268. Operator.CAIRO_OPERATOR_DEST_IN,
  269. Operator.CAIRO_OPERATOR_DEST_OUT,
  270. Operator.CAIRO_OPERATOR_DEST_ATOP,
  271. Operator.CAIRO_OPERATOR_XOR,
  272. Operator.CAIRO_OPERATOR_ADD,
  273. Operator.CAIRO_OPERATOR_SATURATE,
  274. // not supported on RGB24, only RGB32 with alpha.
  275. Operator.CAIRO_OPERATOR_MULTIPLY,
  276. Operator.CAIRO_OPERATOR_SCREEN,
  277. Operator.CAIRO_OPERATOR_OVERLAY,
  278. Operator.CAIRO_OPERATOR_DARKEN,
  279. Operator.CAIRO_OPERATOR_LIGHTEN,
  280. Operator.CAIRO_OPERATOR_COLOR_DODGE,
  281. Operator.CAIRO_OPERATOR_COLOR_BURN,
  282. Operator.CAIRO_OPERATOR_HARD_LIGHT,
  283. Operator.CAIRO_OPERATOR_SOFT_LIGHT,
  284. Operator.CAIRO_OPERATOR_DIFFERENCE,
  285. Operator.CAIRO_OPERATOR_EXCLUSION,
  286. Operator.CAIRO_OPERATOR_HSL_HUE,
  287. Operator.CAIRO_OPERATOR_HSL_SATURATION,
  288. Operator.CAIRO_OPERATOR_HSL_COLOR,
  289. Operator.CAIRO_OPERATOR_HSL_LUMINOSITY
  290. ];
  291. foreach (op; ops)
  292. {
  293. ctx.save();
  294. ctx.rectangle(xIndex * 180, (yIndex * 140), 160, 120);
  295. ctx.clip();
  296. ctx.rectangle(xIndex * 180, (yIndex * 140), 120, 90);
  297. ctx.setSourceRGBA(0.7, 0, 0, 0.8);
  298. ctx.fill();
  299. ctx.setOperator(op);
  300. ctx.rectangle((xIndex * 180) + 40, (yIndex * 140) + 30, 120, 90);
  301. ctx.setSourceRGBA(0, 0, 0.9, 0.4);
  302. ctx.fill();
  303. yIndex++;
  304. if (yIndex == 5)
  305. {
  306. yIndex = 0;
  307. xIndex++;
  308. }
  309. ctx.restore();
  310. ctx.save();
  311. import std.conv;
  312. ctx.setOperator(Operator.CAIRO_OPERATOR_OVER);
  313. ctx.setSourceRGBA(0, 0, 1, 1);
  314. ctx.selectFontFace("Verdana", FontSlant.CAIRO_FONT_SLANT_NORMAL, FontWeight.CAIRO_FONT_WEIGHT_NORMAL);
  315. ctx.setFontSize(10);
  316. ctx.moveTo(10 + (xIndex * 180), 20 + ((yIndex * 140)));
  317. ctx.showText(to!string(op));
  318. ctx.restore();
  319. }
  320. }
  321. }