PageRenderTime 266ms CodeModel.GetById 53ms app.highlight 26ms RepoModel.GetById 184ms app.codeStats 0ms

/win32_clipped_draw.d

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