1 /**
2  * Utility Windows GDI code.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.graphics.gdi;
15 
16 version(Windows):
17 
18 import std.exception;
19 import std.typecons;
20 
21 import ae.sys.windows.imports;
22 mixin(importWin32!(q{wingdi}, q{public}));
23 mixin(importWin32!q{winuser});
24 mixin(importWin32!q{windef});
25 
26 import ae.utils.graphics.color;
27 import ae.utils.graphics.draw;
28 import ae.utils.graphics.view;
29 
30 pragma(lib, "gdi32");
31 
32 /// A canvas with added GDI functionality.
33 struct GDICanvas(COLOR)
34 {
35 	struct Data
36 	{
37 		HDC hdc;
38 		HBITMAP hbm;
39 
40 		@disable this(this);
41 
42 		~this() nothrow @nogc
43 		{
44 			alias DeleteDC_t     = extern(Windows) void function(HDC    ) nothrow @nogc;
45 			alias DeleteObject_t = extern(Windows) void function(HBITMAP) nothrow @nogc;
46 			auto pDeleteDC     = cast(DeleteDC_t    )&DeleteDC;
47 			auto pDeleteObject = cast(DeleteObject_t)&DeleteObject;
48 			pDeleteDC(hdc);     hdc = null;
49 			pDeleteObject(hbm); hbm = null;
50 		}
51 	}
52 
53 	RefCounted!Data data;
54 
55 	int w, h;
56 	COLOR* pixels;
57 
58 	COLOR[] scanline(int y)
59 	{
60 		assert(y>=0 && y<h);
61 		return pixels[w*y..w*(y+1)];
62 	}
63 
64 	mixin DirectView;
65 
66 	this(uint w, uint h)
67 	{
68 		this.w = w;
69 		this.h = h;
70 
71 		auto hddc = GetDC(null);
72 		scope(exit) ReleaseDC(null, hddc);
73 		data.hdc = CreateCompatibleDC(hddc);
74 
75 		BITMAPINFO bmi;
76 		bmi.bmiHeader.biSize        = bmi.bmiHeader.sizeof;
77 		bmi.bmiHeader.biWidth       = w;
78 		bmi.bmiHeader.biHeight      = -h;
79 		bmi.bmiHeader.biPlanes      = 1;
80 		bmi.bmiHeader.biBitCount    = COLOR.sizeof * 8;
81 		bmi.bmiHeader.biCompression = BI_RGB;
82 		void* pvBits;
83 		data.hbm = CreateDIBSection(data.hdc, &bmi, DIB_RGB_COLORS, &pvBits, null, 0);
84 		enforce(data.hbm, "CreateDIBSection");
85 		SelectObject(data.hdc, data.hbm);
86 		pixels = cast(COLOR*)pvBits;
87 	}
88 
89 	auto opDispatch(string F, A...)(A args)
90 		if (is(typeof(mixin(F~"(data.hdc, args)"))))
91 	{
92 		mixin("return "~F~"(data.hdc, args);");
93 	}
94 }
95 
96 unittest
97 {
98 	alias RGB = ae.utils.graphics.color.RGB;
99 
100 //	alias BGR COLOR;
101 	alias BGRX COLOR;
102 	auto b = GDICanvas!COLOR(100, 100);
103 	b.fill(COLOR(255, 255, 255));
104 
105 	const str = "Hello, world!";
106 	auto f = CreateFont(-11, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Tahoma"); scope(exit) DeleteObject(f);
107 	b.SelectObject(f);
108 	b.SetBkColor(0xFFFFFF);
109 	b.SetTextColor(0x0000FF);
110 	b.TextOutA(10, 10, str.ptr, cast(uint)str.length);
111 
112 	b.SetPixel(5, 5, 0xFF0000);
113 	GdiFlush();
114 	b[6, 6] = COLOR(255, 0, 0);
115 
116 	import ae.utils.graphics.image;
117 	auto i = b.copy.colorMap!(c => RGB(c.r,c.g,c.b))();
118 	assert(i[5, 5] == RGB(0, 0, 255));
119 	assert(i[6, 6] == RGB(0, 0, 255));
120 
121 //	i.savePNG("gditest.png");
122 //	i.savePNM("gditest.pnm");
123 }