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 public import win32.wingdi;
22 import win32.winuser;
23 import win32.windef;
24 
25 import ae.utils.graphics.color;
26 import ae.utils.graphics.draw;
27 import ae.utils.graphics.view;
28 
29 /// A canvas with added GDI functionality.
30 struct GDICanvas(COLOR)
31 {
32 	struct Data
33 	{
34 		HDC hdc;
35 		HBITMAP hbm;
36 
37 		@disable this(this);
38 
39 		~this()
40 		{
41 			DeleteDC(hdc);     hdc = null;
42 			DeleteObject(hbm); hbm = null;
43 		}
44 	}
45 
46 	RefCounted!Data data;
47 
48 	int w, h;
49 	COLOR* pixels;
50 
51 	COLOR[] scanline(int y)
52 	{
53 		assert(y>=0 && y<h);
54 		return pixels[w*y..w*(y+1)];
55 	}
56 
57 	mixin DirectView;
58 
59 	this(uint w, uint h)
60 	{
61 		this.w = w;
62 		this.h = h;
63 
64 		auto hddc = GetDC(null);
65 		scope(exit) ReleaseDC(null, hddc);
66 		data.hdc = CreateCompatibleDC(hddc);
67 
68 		BITMAPINFO bmi;
69 		bmi.bmiHeader.biSize        = bmi.bmiHeader.sizeof;
70 		bmi.bmiHeader.biWidth       = w;
71 		bmi.bmiHeader.biHeight      = -h;
72 		bmi.bmiHeader.biPlanes      = 1;
73 		bmi.bmiHeader.biBitCount    = COLOR.sizeof * 8;
74 		bmi.bmiHeader.biCompression = BI_RGB;
75 		void* pvBits;
76 		data.hbm = CreateDIBSection(data.hdc, &bmi, DIB_RGB_COLORS, &pvBits, null, 0);
77 		enforce(data.hbm, "CreateDIBSection");
78 		SelectObject(data.hdc, data.hbm);
79 		pixels = cast(COLOR*)pvBits;
80 	}
81 
82 	auto opDispatch(string F, A...)(A args)
83 		if (is(typeof(mixin(F~"(data.hdc, args)"))))
84 	{
85 		mixin("return "~F~"(data.hdc, args);");
86 	}
87 }
88 
89 unittest
90 {
91 	alias RGB = ae.utils.graphics.color.RGB;
92 
93 //	alias BGR COLOR;
94 	alias BGRX COLOR;
95 	auto b = GDICanvas!COLOR(100, 100);
96 	b.clear(COLOR(255, 255, 255));
97 
98 	const str = "Hello, world!";
99 	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);
100 	b.SelectObject(f);
101 	b.SetBkColor(0xFFFFFF);
102 	b.SetTextColor(0x0000FF);
103 	b.TextOutA(10, 10, str.ptr, cast(uint)str.length);
104 
105 	b.SetPixel(5, 5, 0xFF0000);
106 	GdiFlush();
107 	b[6, 6] = COLOR(255, 0, 0);
108 
109 	import ae.utils.graphics.image;
110 	auto i = b.copy.colorMap!(c => RGB(c.r,c.g,c.b))();
111 	assert(i[5, 5] == RGB(0, 0, 255));
112 	assert(i[6, 6] == RGB(0, 0, 255));
113 
114 //	i.savePNG("gditest.png");
115 //	i.savePNM("gditest.pnm");
116 }