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