/win32_cairo_operator.d
http://github.com/AndrejMitrovic/cairoDSamples · D · 381 lines · 265 code · 60 blank · 56 comment · 11 complexity · 4f6cdb387ed88b8139c37aa92e480892 MD5 · raw file
- module win32_cairo_doublebuffer;
- /+
- + Copyright Andrej Mitrovic 2011.
- + Distributed under the Boost Software License, Version 1.0.
- + (See accompanying file LICENSE_1_0.txt or copy at
- + http://www.boost.org/LICENSE_1_0.txt)
- +/
- import std.utf : toUTFz;
- alias toUTFz!(const(wchar)*, string) toUTF16z;
- alias toUTFz!(const(wchar)*, wstring) toUTF16z;
- alias toUTFz!(const(wchar)*, dstring) toUTF16z;
- /+
- + Demonstrates the usage of Cairo compositing operators and alpha-blitting.
- +
- + Note: Other samples in this directory do not use alpha-blending, and should
- + probably be rewritten. Sorry for that! :)
- +
- + Notes:
- + You have to use an RGB32 surface type, otherwise you won't be able
- + to use numerous alpha-blending operators (you will get a runtime exception).
- +
- + I'm using CairoD's Win32Surface ctor that can create a surface type
- + based on the format enum supplied. Alternatively I could have manually
- + created a DIBSection and construct a Win32Surface with that.
- +
- + I'm using AlphaBlend (symbol name is actually 'GdiAlphaBlend' in Gdi32.lib),
- + if you get undefined symbol errors it could mean the Gdi32.lib import lib
- + distributed with DMD/GDC is outdated. You can create a new OMF-compatible
- + one by grabbing the Windows SDK, downloading coffimplib
- + (ftp://ftp.digitalmars.com/coffimplib.zip), and browsing to
- + where Gdi32.lib is located, e.g.:
- +
- + C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib
- +
- + and calling:
- + coffimplib Gdi32_coff.lib gdi32.lib
- +
- + Then copy 'Gdi32_coff.lib' to DMD\dmd2\windows\lib\, delete the old 'gdi32.lib',
- + and rename 'Gdi32_coff.lib' to 'gdi32.lib'.
- +
- + I use 2 paint buffers, one is the foreground that only paints to certain
- + regions and has an alpha, the other acts as a background with its entire
- + surface painted white with alpha 1.0 (max).
- +
- + I use AlphaBlit to blit the foreground to the background. AlphaBlit is set
- + to blit by using per-pixel alpha values (this is configurable to other settings).
- +
- + The reason I'm using 2 paint buffers and not just 1 foreground buffer and
- + a white pre-painted window device context is because the latter usually introduces
- + graphical glitches. For example, painting the window white directly via a
- + device context (and e.g. the GDI FillRect function) and then alpha-blitting the
- + foreground results in 2 refresh events. This would introduce flicker effects,
- + so it's safer to keep a separate background buffer to blend with when drawing,
- + and then blit to the screen as necessary.
- +/
- import core.runtime;
- import std.process;
- import std.stdio;
- //~ import std.utf;
- pragma(lib, "gdi32.lib");
- import win32.windef;
- import win32.winuser;
- import win32.wingdi;
- string appName = "CairoWindow";
- string description = "A simple win32 window with Cairo drawing";
- HINSTANCE hinst;
- import cairo.cairo;
- import cairo.win32;
- alias cairo.cairo.RGB RGB; // conflicts with win32.wingdi.RGB
- struct AlphaBlendType
- {
- static normal = BLENDFUNCTION(AC_SRC_OVER, 0, 255, AC_SRC_ALPHA);
- }
- extern(Windows) BOOL GdiAlphaBlend(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);
- void AlphaBlit(HDC dstHdc, HDC srcHdc, int width, int height, BLENDFUNCTION blendType = AlphaBlendType.normal)
- {
- auto result = GdiAlphaBlend(dstHdc, 0, 0, width, height,
- srcHdc, 0, 0, width, height, blendType);
- if (result == 0)
- {
- //~ auto error = GetLastError();
- //~ msgbox(error);
- }
- }
-
- extern (Windows)
- int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
- {
- int result;
- void exceptionHandler(Throwable e) { throw e; }
- try
- {
- Runtime.initialize(&exceptionHandler);
- result = myWinMain(hInstance, hPrevInstance, lpCmdLine, iCmdShow);
- Runtime.terminate(&exceptionHandler);
- }
- catch (Throwable o)
- {
- MessageBox(null, o.toString().toUTF16z, "Error", MB_OK | MB_ICONEXCLAMATION);
- result = 0;
- }
- return result;
- }
- int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
- {
- hinst = hInstance;
- HACCEL hAccel;
- HWND hwnd;
- MSG msg;
- WNDCLASS wndclass;
- // commented out so we do not redraw the entire screen on resize
- //~ wndclass.style = CS_HREDRAW | CS_VREDRAW;
- wndclass.style = WS_CLIPCHILDREN;
- wndclass.lpfnWndProc = &WndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = hInstance;
- wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
- wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
- wndclass.hbrBackground = null;
- wndclass.lpszMenuName = appName.toUTF16z;
- wndclass.lpszClassName = appName.toUTF16z;
- if (!RegisterClass(&wndclass))
- {
- MessageBox(NULL, "This program requires Windows NT!", appName.toUTF16z, MB_ICONERROR);
- return 0;
- }
- hwnd = CreateWindow(appName.toUTF16z, // window class name
- description.toUTF16z, // window caption
- WS_OVERLAPPEDWINDOW, // window style
- (1680 - 900) / 2, // initial x position
- (1050 - 700) / 2, // initial y position
- 1100, // initial x size
- 800, // initial y size
- NULL, // parent window handle
- NULL, // window menu handle
- hInstance, // program instance handle
- NULL); // creation parameters
- ShowWindow(hwnd, iCmdShow);
- UpdateWindow(hwnd);
- while (GetMessage(&msg, NULL, 0, 0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- return msg.wParam;
- }
- extern (Windows)
- LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch (message)
- {
- case WM_CREATE:
- {
- window = new Window(hwnd);
- return 0;
- }
- default:
- }
- if (window)
- return window.process(hwnd, message, wParam, lParam);
- else
- return DefWindowProc(hwnd, message, wParam, lParam);
- }
- Window window;
- struct PaintBuffer
- {
- int width, height;
- this(HDC localHdc, int cxClient, int cyClient)
- {
- surf = new Win32Surface(Format.CAIRO_FORMAT_ARGB32, cxClient, cyClient);
- ctx = Context(surf);
- initialized = true;
- }
- ~this()
- {
- if (initialized) // struct dtors are still buggy sometimes
- {
- ctx.dispose();
- surf.finish();
- surf.dispose();
- initialized = false;
- }
- }
- bool initialized;
- Context ctx;
- Win32Surface surf;
- }
- class Window
- {
- int width, height;
- HWND hwnd;
- PAINTSTRUCT ps;
- PaintBuffer paintBuffer;
- PaintBuffer paintBufferNew;
- bool needsRedraw;
- this(HWND hwnd)
- {
- this.hwnd = hwnd;
- auto hDesk = GetDesktopWindow();
- RECT rc;
- GetClientRect(hDesk, &rc);
- auto localHdc = GetDC(hwnd);
- paintBuffer = PaintBuffer(localHdc, rc.right, rc.bottom);
- paintBufferNew = PaintBuffer(localHdc, rc.right, rc.bottom);
- needsRedraw = true;
- }
- LRESULT process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch (message)
- {
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
- case WM_PAINT:
- OnPaint(hwnd, message, wParam, lParam);
- return 0;
- case WM_ERASEBKGND:
- return 1;
- case WM_SIZE:
- {
- width = LOWORD(lParam);
- height = HIWORD(lParam);
- return 0;
- }
- default:
- }
- return DefWindowProc(hwnd, message, wParam, lParam);
- }
- void OnPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- static int blitCount;
- auto hdc = BeginPaint(hwnd, &ps);
- auto ctx = paintBuffer.ctx;
- auto hBuffer = paintBuffer.surf.getDC;
-
- auto boundRect = ps.rcPaint;
- if (needsRedraw) // cairo needs to redraw
- {
- draw(ctx);
-
- with (boundRect)
- {
- paintBufferNew.ctx.setSourceRGBA(1, 1, 1, 1);
- paintBufferNew.ctx.paint();
-
- AlphaBlit(paintBufferNew.surf.getDC(), hBuffer, right - left, bottom - top);
- }
-
- needsRedraw = false;
- }
- with (boundRect) // blit only required areas
- {
- BitBlt(hdc, left, top, right, bottom, paintBufferNew.surf.getDC(), left, top, SRCCOPY);
- }
- EndPaint(hwnd, &ps);
- }
-
- void draw(Context ctx)
- {
- import std.traits;
- static size_t yIndex;
- static size_t xIndex;
-
- auto ops =
- [
- Operator.CAIRO_OPERATOR_CLEAR,
- Operator.CAIRO_OPERATOR_SOURCE,
- Operator.CAIRO_OPERATOR_OVER,
- Operator.CAIRO_OPERATOR_IN,
- Operator.CAIRO_OPERATOR_OUT,
- Operator.CAIRO_OPERATOR_ATOP,
- Operator.CAIRO_OPERATOR_DEST,
- Operator.CAIRO_OPERATOR_DEST_OVER,
- Operator.CAIRO_OPERATOR_DEST_IN,
- Operator.CAIRO_OPERATOR_DEST_OUT,
- Operator.CAIRO_OPERATOR_DEST_ATOP,
- Operator.CAIRO_OPERATOR_XOR,
- Operator.CAIRO_OPERATOR_ADD,
- Operator.CAIRO_OPERATOR_SATURATE,
-
- // not supported on RGB24, only RGB32 with alpha.
- Operator.CAIRO_OPERATOR_MULTIPLY,
- Operator.CAIRO_OPERATOR_SCREEN,
- Operator.CAIRO_OPERATOR_OVERLAY,
- Operator.CAIRO_OPERATOR_DARKEN,
- Operator.CAIRO_OPERATOR_LIGHTEN,
- Operator.CAIRO_OPERATOR_COLOR_DODGE,
- Operator.CAIRO_OPERATOR_COLOR_BURN,
- Operator.CAIRO_OPERATOR_HARD_LIGHT,
- Operator.CAIRO_OPERATOR_SOFT_LIGHT,
- Operator.CAIRO_OPERATOR_DIFFERENCE,
- Operator.CAIRO_OPERATOR_EXCLUSION,
- Operator.CAIRO_OPERATOR_HSL_HUE,
- Operator.CAIRO_OPERATOR_HSL_SATURATION,
- Operator.CAIRO_OPERATOR_HSL_COLOR,
- Operator.CAIRO_OPERATOR_HSL_LUMINOSITY
- ];
-
- foreach (op; ops)
- {
- ctx.save();
- ctx.rectangle(xIndex * 180, (yIndex * 140), 160, 120);
- ctx.clip();
-
- ctx.rectangle(xIndex * 180, (yIndex * 140), 120, 90);
- ctx.setSourceRGBA(0.7, 0, 0, 0.8);
- ctx.fill();
- ctx.setOperator(op);
-
- ctx.rectangle((xIndex * 180) + 40, (yIndex * 140) + 30, 120, 90);
- ctx.setSourceRGBA(0, 0, 0.9, 0.4);
- ctx.fill();
-
- yIndex++;
-
- if (yIndex == 5)
- {
- yIndex = 0;
- xIndex++;
- }
-
- ctx.restore();
-
- ctx.save();
- import std.conv;
- ctx.setOperator(Operator.CAIRO_OPERATOR_OVER);
-
- ctx.setSourceRGBA(0, 0, 1, 1);
- ctx.selectFontFace("Verdana", FontSlant.CAIRO_FONT_SLANT_NORMAL, FontWeight.CAIRO_FONT_WEIGHT_NORMAL);
- ctx.setFontSize(10);
- ctx.moveTo(10 + (xIndex * 180), 20 + ((yIndex * 140)));
- ctx.showText(to!string(op));
- ctx.restore();
- }
- }
- }