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