1 module dfl.clippingform;
2 
3 private import dfl.all, dfl.internal.winapi;
4 private import core.memory;
5 
6 private extern (Windows)
7 {
8 	struct RGNDATAHEADER
9 	{
10 		DWORD dwSize;
11 		DWORD iType;
12 		DWORD nCount;
13 		DWORD nRgnSize;
14 		RECT rcBound;
15 	}
16 	
17 	struct RGNDATA
18 	{
19 		RGNDATAHEADER rdh;
20 		ubyte[1] Buffer;
21 	}
22 	
23 	struct XFORM
24 	{
25 		FLOAT eM11;
26 		FLOAT eM12;
27 		FLOAT eM21;
28 		FLOAT eM22;
29 		FLOAT eDx;
30 		FLOAT eDy;
31 	}
32 	
33 	enum {RDH_RECTANGLES = 1}
34 	enum {BI_RGB = 0}
35 	enum {DIB_RGB_COLORS = 0}
36 	
37 	HRGN ExtCreateRegion(void*, DWORD, RGNDATA*);
38 	int GetDIBits(HDC, HBITMAP, UINT, UINT, PVOID, LPBITMAPINFO, UINT);
39 }
40 
41 
42 ///
43 struct RegionRects
44 {
45 private:
46 	RGNDATA* _rgn = null;
47 	size_t _capacity = 0;
48 	size_t _width = 0;
49 	size_t _height = 0;
50 public:
51 	
52 	
53 	const @property
54 	size_t width()
55 	{
56 		return _width;
57 	}
58 	
59 	
60 	///
61 	const @property
62 	size_t height()
63 	{
64 		return _height;
65 	}
66 	
67 	
68 	///
69 	void clear()
70 	{
71 		if (_rgn)
72 		{
73 			GC.free(_rgn);
74 		}
75 		_rgn = null;
76 		_capacity = 0;
77 		_width = 0;
78 		_height = 0;
79 	}
80 	
81 	
82 	///
83 	void add(RECT rc)
84 	{
85 		if (_capacity == 0)
86 		{
87 			_capacity = 1024;
88 			_rgn = cast(RGNDATA*) GC.malloc(
89 				RGNDATAHEADER.sizeof + RECT.sizeof * _capacity);
90 			_rgn.rdh.nCount = 0;
91 		}
92 		else if (_rgn.rdh.nCount == _capacity)
93 		{
94 			_capacity *= 2;
95 			_rgn = cast(RGNDATA*) GC.realloc(cast(void*)_rgn,
96 				RGNDATAHEADER.sizeof + RECT.sizeof * _capacity);
97 		}
98 		(cast(RECT*)_rgn.Buffer.ptr)[_rgn.rdh.nCount++] = rc;
99 	}
100 	
101 	
102 	/// ditto
103 	void add(int l, int t, int r, int b)
104 	{
105 		add(RECT(l, t, r, b));
106 	}
107 	
108 	
109 	/// ditto
110 	void opCatAssign(RECT rc)
111 	{
112 		add(rc);
113 	}
114 	
115 	
116 	///
117 	@property
118 	Region region()
119 	{
120 		if (_rgn is null) return null;
121 		with (_rgn.rdh)
122 		{
123 			dwSize = RGNDATAHEADER.sizeof;
124 			iType  = RDH_RECTANGLES;
125 			nRgnSize = RGNDATAHEADER.sizeof + RECT.sizeof*nCount;
126 			rcBound = RECT(0,0,_width,_height);
127 		}
128 		if (auto hRgn = ExtCreateRegion(null, _rgn.rdh.nRgnSize, _rgn))
129 		{
130 			return new Region(hRgn);
131 		}
132 		throw new Exception("Failed to make a region data.");
133 	}
134 	
135 	
136 	private Region createClippingRegionFromHDC(HBITMAP hBitmap)
137 	{
138 		HDC hDC = CreateCompatibleDC(null);
139 		auto h = _height;
140 		auto w = _width;
141 		if (!hDC) throw new Exception("Failed to get device context data.");
142 		BITMAPINFOHEADER bi;
143 		with(bi)
144 		{
145 			biSize        = BITMAPINFOHEADER.sizeof;
146 			biWidth       = w;
147 			biHeight      = h;
148 			biPlanes      = 1;
149 			biBitCount    = 32;
150 			biCompression = BI_RGB;
151 		}
152 		auto pxs = new COLORREF[w];
153 		COLORREF tr;
154 		for (int y = 1; y < h; ++y)
155 		{
156 			GetDIBits(hDC, hBitmap, h-y, 1, pxs.ptr, cast(BITMAPINFO*)&bi, DIB_RGB_COLORS);
157 			if (y == 1) tr = pxs[0];
158 			for (int x = 0; x < w; x++)
159 			{
160 				if (pxs[x] == tr) continue;
161 				int sx = x;
162 				while (x < w)
163 				{
164 					if (pxs[x++] == tr) break;
165 				}
166 				add(sx, y-1, x-1, y);
167 			}
168 		}
169 		return region;
170 	}
171 	
172 	
173 	///
174 	Region create(MemoryGraphics g)
175 	{
176 		clear();
177 		_width = g.width;
178 		_height = g.height;
179 		return createClippingRegionFromHDC(cast(HBITMAP)g.hbitmap);
180 	}
181 	
182 	
183 	/// ditto
184 	Region create(Image img)
185 	{
186 		clear();
187 		_width = img.width;
188 		_height = img.height;
189 		if (auto bmp = cast(Bitmap)img)
190 		{
191 			return createClippingRegionFromHDC(cast(HBITMAP)bmp.handle);
192 		}
193 		auto g = new MemoryGraphics(img.width, img.height);
194 		img.draw(g, Point(0,0));
195 		return create(g);
196 	}
197 }
198 
199 
200 ///
201 class ClippingForm: Form
202 {
203 private:
204 	Image m_Image;
205 	RegionRects m_RegionRects;
206 protected:
207 	override void createParams(ref CreateParams cp)
208 	{
209 		super.createParams(cp);
210 		cp.style = WS_EX_TOPMOST | WS_EX_TOOLWINDOW;
211 	}
212 public:
213 	
214 	
215 	///
216 	@property Image clipping()
217 	{
218 		return m_Image;
219 	}
220 	
221 	
222 	/// ditto
223 	@property void clipping(Image img)
224 	{
225 		m_Image = img;
226 	}
227 	
228 	
229 	///
230 	override void onHandleCreated(EventArgs ea)
231 	{
232 		if (m_Image)
233 		{
234 			region = m_RegionRects.create(m_Image);
235 		}
236 		super.onHandleCreated(ea);
237 	}
238 	
239 	
240 	///
241 	override void onPaint(PaintEventArgs pea)
242 	{
243 		if (m_Image)
244 		{
245 			m_Image.draw(pea.graphics, Point(0,0));
246 		}
247 		else
248 		{
249 			super.onPaint(pea);
250 		}
251 	}
252 }