PageRenderTime 153ms CodeModel.GetById 35ms app.highlight 64ms RepoModel.GetById 17ms app.codeStats 20ms

/win32_cairo_operator.d

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