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