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 }