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 }