/
gdi.d
156 lines (129 loc) · 3.98 KB
/
gdi.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* Utility Windows GDI code.
*
* License:
* This Source Code Form is subject to the terms of
* the Mozilla Public License, v. 2.0. If a copy of
* the MPL was not distributed with this file, You
* can obtain one at http://mozilla.org/MPL/2.0/.
*
* Authors:
* Vladimir Panteleev <ae@cy.md>
*/
module ae.utils.graphics.gdi;
version(Windows):
import std.exception;
import std.typecons;
import ae.sys.windows.imports;
mixin(importWin32!(q{wingdi}, q{public}));
mixin(importWin32!q{winuser});
mixin(importWin32!q{windef});
import ae.utils.graphics.color;
import ae.utils.graphics.draw;
import ae.utils.graphics.image : bitmapPixelStride;
import ae.utils.graphics.view;
pragma(lib, "gdi32");
/// A canvas with added GDI functionality.
struct GDICanvas(COLOR)
{
/// Storage type used by the GDI buffer.
static if (is(COLOR == bool))
alias StorageType = OneBitStorageBE;
else
alias StorageType = PlainStorageUnit!COLOR;
/// Wraps and owns the Windows API objects.
struct Data
{
HDC hdc; /// Windows GDI DC handle.
HBITMAP hbm; /// Windows GDI bitmap handle.
@disable this(this);
~this() nothrow @nogc
{
alias DeleteDC_t = extern(Windows) void function(HDC ) nothrow @nogc;
alias DeleteObject_t = extern(Windows) void function(HBITMAP) nothrow @nogc;
auto pDeleteDC = cast(DeleteDC_t )&DeleteDC;
auto pDeleteObject = cast(DeleteObject_t)&DeleteObject;
pDeleteDC(hdc); hdc = null;
pDeleteObject(hbm); hbm = null;
}
}
RefCounted!Data data; /// Reference to the Windows API objects.
/// Geometry.
xy_t w, h;
private StorageType* pixelData;
private sizediff_t pixelStride;
/// `DirectView` interface.
inout(StorageType)[] scanline(xy_t y) inout
{
assert(y>=0 && y<h);
auto row = cast(void*)pixelData + y * pixelStride;
auto storageUnitsPerRow = (w + StorageType.length - 1) / StorageType.length;
return (cast(inout(StorageType)*)row)[0 .. storageUnitsPerRow];
}
mixin DirectView;
this(uint w, uint h)
{
this.w = w;
this.h = h;
auto hddc = GetDC(null);
scope(exit) ReleaseDC(null, hddc);
data.hdc = CreateCompatibleDC(hddc);
BITMAPINFO bmi;
bmi.bmiHeader.biSize = bmi.bmiHeader.sizeof;
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = StorageType.sizeof * 8 / StorageType.length;
bmi.bmiHeader.biCompression = BI_RGB;
void* pvBits;
data.hbm = CreateDIBSection(data.hdc, &bmi, DIB_RGB_COLORS, &pvBits, null, 0);
enforce(data.hbm && pvBits, "CreateDIBSection");
SelectObject(data.hdc, data.hbm);
pixelData = cast(StorageType*)pvBits;
pixelStride = bitmapPixelStride!StorageType(w);
} ///
/// Forwards Windows GDI calls.
auto opDispatch(string F, A...)(A args)
if (is(typeof(mixin(F~"(data.hdc, args)"))))
{
mixin("return "~F~"(data.hdc, args);");
}
}
///
version(ae_unittest) unittest
{
alias RGB = ae.utils.graphics.color.RGB;
// alias BGR COLOR;
alias BGRX COLOR;
auto b = GDICanvas!COLOR(100, 100);
b.fill(COLOR(255, 255, 255));
const str = "Hello, world!";
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);
b.SelectObject(f);
b.SetBkColor(0xFFFFFF);
b.SetTextColor(0x0000FF);
b.TextOutA(10, 10, str.ptr, cast(uint)str.length);
b.SetPixel(5, 5, 0xFF0000);
GdiFlush();
b[6, 6] = COLOR(255, 0, 0);
import ae.utils.graphics.image;
auto i = b.copy.colorMap!(c => RGB(c.r,c.g,c.b))();
assert(i[5, 5] == RGB(0, 0, 255));
assert(i[6, 6] == RGB(0, 0, 255));
assert(i[7, 7] == RGB(255, 255, 255));
// i.savePNG("gditest.png");
// i.savePNM("gditest.pnm");
}
version(ae_unittest) unittest
{
auto b = GDICanvas!bool(100, 100);
b.fill(true);
b.SetPixel(5, 5, 0x000000);
GdiFlush();
b[6, 6] = false;
import ae.utils.graphics.image : copy;
auto i = b.copy.colorMap!(c => L8(c * 0xFF))();
assert(i[5, 5] == L8(0));
assert(i[6, 6] == L8(0));
assert(i[7, 7] == L8(255));
}