1 /*
2  * PROJECT:     PAINT for ReactOS
3  * LICENSE:     LGPL
4  * FILE:        base/applications/mspaint/selectionmodel.cpp
5  * PURPOSE:     Keep track of selection parameters, notify listeners
6  * PROGRAMMERS: Benedikt Freisen
7  *              Katayama Hirofumi MZ
8  */
9 
10 /* INCLUDES *********************************************************/
11 
12 #include "precomp.h"
13 
14 /* FUNCTIONS ********************************************************/
15 
16 SelectionModel::SelectionModel()
17     : m_hDC(CreateCompatibleDC(NULL))
18     , m_hBm(NULL)
19     , m_hMask(NULL)
20     , m_ptStack(NULL)
21     , m_iPtSP(0)
22 {
23     SetRectEmpty(&m_rcSrc);
24     SetRectEmpty(&m_rcDest);
25 }
26 
27 SelectionModel::~SelectionModel()
28 {
29     DeleteDC(m_hDC);
30     ResetPtStack();
31     if (m_hBm)
32     {
33         DeleteObject(m_hBm);
34     }
35     if (m_hMask)
36     {
37         DeleteObject(m_hMask);
38     }
39 }
40 
41 void SelectionModel::ResetPtStack()
42 {
43     if (m_ptStack != NULL)
44         HeapFree(GetProcessHeap(), 0, m_ptStack);
45     m_ptStack = NULL;
46     m_iPtSP = 0;
47 }
48 
49 void SelectionModel::PushToPtStack(LONG x, LONG y)
50 {
51     if (m_iPtSP % 1024 == 0)
52     {
53         if (m_ptStack)
54             m_ptStack = (POINT*) HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, m_ptStack, sizeof(POINT) * (m_iPtSP + 1024));
55         else
56             m_ptStack = (POINT*) HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(POINT) * 1024);
57     }
58     m_ptStack[m_iPtSP].x = x;
59     m_ptStack[m_iPtSP].y = y;
60     m_iPtSP++;
61 }
62 
63 void SelectionModel::CalculateBoundingBoxAndContents(HDC hDCImage)
64 {
65     int i;
66     m_rcSrc.left = m_rcSrc.top = MAXLONG;
67     m_rcSrc.right = m_rcSrc.bottom = 0;
68     for (i = 0; i < m_iPtSP; i++)
69     {
70         if (m_ptStack[i].x < m_rcSrc.left)
71             m_rcSrc.left = m_ptStack[i].x;
72         if (m_ptStack[i].y < m_rcSrc.top)
73             m_rcSrc.top = m_ptStack[i].y;
74         if (m_ptStack[i].x > m_rcSrc.right)
75             m_rcSrc.right = m_ptStack[i].x;
76         if (m_ptStack[i].y > m_rcSrc.bottom)
77             m_rcSrc.bottom = m_ptStack[i].y;
78     }
79     m_rcSrc.right  += 1;
80     m_rcSrc.bottom += 1;
81     m_rcDest.left   = m_rcSrc.left;
82     m_rcDest.top    = m_rcSrc.top;
83     m_rcDest.right  = m_rcSrc.right;
84     m_rcDest.bottom = m_rcSrc.bottom;
85 
86     if (m_iPtSP > 1)
87     {
88         DeleteObject(m_hMask);
89         m_hMask = CreateBitmap(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), 1, 1, NULL);
90         DeleteObject(SelectObject(m_hDC, m_hMask));
91         POINT *m_ptStackCopy = (POINT*) HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(POINT) * m_iPtSP);
92         for (i = 0; i < m_iPtSP; i++)
93         {
94             m_ptStackCopy[i].x = m_ptStack[i].x - m_rcSrc.left;
95             m_ptStackCopy[i].y = m_ptStack[i].y - m_rcSrc.top;
96         }
97         Poly(m_hDC, m_ptStackCopy, m_iPtSP, 0x00ffffff, 0x00ffffff, 1, 2, TRUE, FALSE);
98         HeapFree(GetProcessHeap(), 0, m_ptStackCopy);
99         SelectObject(m_hDC, m_hBm = CreateDIBWithProperties(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc)));
100         imageModel.ResetToPrevious();
101         MaskBlt(m_hDC, 0, 0, RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), hDCImage, m_rcSrc.left,
102                 m_rcSrc.top, m_hMask, 0, 0, MAKEROP4(SRCCOPY, WHITENESS));
103     }
104 }
105 
106 void SelectionModel::CalculateContents(HDC hDCImage)
107 {
108     DeleteObject(m_hMask);
109     m_hMask = CreateBitmap(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), 1, 1, NULL);
110     DeleteObject(SelectObject(m_hDC, m_hMask));
111     Rect(m_hDC, 0, 0, RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), 0x00ffffff, 0x00ffffff, 1, 2);
112     SelectObject(m_hDC, m_hBm = CreateDIBWithProperties(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc)));
113     BitBlt(m_hDC, 0, 0, RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), hDCImage, m_rcSrc.left,
114            m_rcSrc.top, SRCCOPY);
115 }
116 
117 void SelectionModel::DrawBackgroundPoly(HDC hDCImage, COLORREF crBg)
118 {
119     Poly(hDCImage, m_ptStack, m_iPtSP, crBg, crBg, 1, 2, TRUE, FALSE);
120 }
121 
122 void SelectionModel::DrawBackgroundRect(HDC hDCImage, COLORREF crBg)
123 {
124     Rect(hDCImage, m_rcSrc.left, m_rcSrc.top, m_rcSrc.right, m_rcSrc.bottom, crBg, crBg, 0, 1);
125 }
126 
127 extern BOOL
128 ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, HBITMAP hbmMask, int xMask, int yMask, DWORD dwRop, COLORREF keyColor);
129 
130 void SelectionModel::DrawSelection(HDC hDCImage, COLORREF crBg, BOOL bBgTransparent)
131 {
132     if (!bBgTransparent)
133         MaskBlt(hDCImage, m_rcDest.left, m_rcDest.top, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest),
134                 m_hDC, 0, 0, m_hMask, 0, 0, MAKEROP4(SRCCOPY, SRCAND));
135     else
136         ColorKeyedMaskBlt(hDCImage, m_rcDest.left, m_rcDest.top, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest),
137                           m_hDC, 0, 0, m_hMask, 0, 0, MAKEROP4(SRCCOPY, SRCAND), crBg);
138 }
139 
140 void SelectionModel::DrawSelectionStretched(HDC hDCImage)
141 {
142     StretchBlt(hDCImage, m_rcDest.left, m_rcDest.top, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC, 0, 0, GetDIBWidth(m_hBm), GetDIBHeight(m_hBm), SRCCOPY);
143 }
144 
145 void SelectionModel::ScaleContentsToFit()
146 {
147     HDC hTempDC;
148     HBITMAP hTempBm;
149     hTempDC = CreateCompatibleDC(m_hDC);
150     hTempBm = CreateDIBWithProperties(RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest));
151     SelectObject(hTempDC, hTempBm);
152     SelectObject(m_hDC, m_hBm);
153     StretchBlt(hTempDC, 0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC, 0, 0,
154                GetDIBWidth(m_hBm), GetDIBHeight(m_hBm), SRCCOPY);
155     DeleteObject(m_hBm);
156     m_hBm = hTempBm;
157     hTempBm = CreateBitmap(RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), 1, 1, NULL);
158     SelectObject(hTempDC, hTempBm);
159     SelectObject(m_hDC, m_hMask);
160     StretchBlt(hTempDC, 0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC, 0, 0,
161                GetDIBWidth(m_hMask), GetDIBHeight(m_hMask), SRCCOPY);
162     DeleteObject(m_hMask);
163     m_hMask = hTempBm;
164     SelectObject(m_hDC, m_hBm);
165     DeleteDC(hTempDC);
166 }
167 
168 void SelectionModel::InsertFromHBITMAP(HBITMAP hBm)
169 {
170     HDC hTempDC;
171     HBITMAP hTempMask;
172 
173     DeleteObject(SelectObject(m_hDC, m_hBm = (HBITMAP) CopyImage(hBm,
174                                                                  IMAGE_BITMAP, 0, 0,
175                                                                  LR_COPYRETURNORG)));
176 
177     SetRectEmpty(&m_rcSrc);
178     m_rcDest.left = m_rcDest.top = 0;
179     m_rcDest.right = m_rcDest.left + GetDIBWidth(m_hBm);
180     m_rcDest.bottom = m_rcDest.top + GetDIBHeight(m_hBm);
181 
182     hTempDC = CreateCompatibleDC(m_hDC);
183     hTempMask = CreateBitmap(RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), 1, 1, NULL);
184     SelectObject(hTempDC, hTempMask);
185     Rect(hTempDC, m_rcDest.left, m_rcDest.top, m_rcDest.right, m_rcDest.bottom, 0x00ffffff, 0x00ffffff, 1, 1);
186     DeleteObject(m_hMask);
187     m_hMask = hTempMask;
188     DeleteDC(hTempDC);
189 }
190 
191 void SelectionModel::FlipHorizontally()
192 {
193     SelectObject(m_hDC, m_hMask);
194     StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, 0, -RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC,
195                0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
196     SelectObject(m_hDC, m_hBm);
197     StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, 0, -RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC,
198                0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
199     NotifyRefreshNeeded();
200 }
201 
202 void SelectionModel::FlipVertically()
203 {
204     SelectObject(m_hDC, m_hMask);
205     StretchBlt(m_hDC, 0, RECT_HEIGHT(m_rcDest) - 1, RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
206                0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
207     SelectObject(m_hDC, m_hBm);
208     StretchBlt(m_hDC, 0, RECT_HEIGHT(m_rcDest) - 1, RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
209                0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
210     NotifyRefreshNeeded();
211 }
212 
213 void SelectionModel::RotateNTimes90Degrees(int iN)
214 {
215     if (iN == 2)
216     {
217         SelectObject(m_hDC, m_hMask);
218         StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, RECT_HEIGHT(m_rcDest) - 1, -RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
219                    0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
220         SelectObject(m_hDC, m_hBm);
221         StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, RECT_HEIGHT(m_rcDest) - 1, -RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
222                    0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
223     }
224     NotifyRefreshNeeded();
225 }
226 
227 HBITMAP SelectionModel::GetBitmap()
228 {
229     return m_hBm;
230 }
231 
232 int SelectionModel::PtStackSize()
233 {
234     return m_iPtSP;
235 }
236 
237 void SelectionModel::DrawFramePoly(HDC hDCImage)
238 {
239     Poly(hDCImage, m_ptStack, m_iPtSP, 0, 0, 2, 0, FALSE, TRUE); /* draw the freehand selection inverted/xored */
240 }
241 
242 void SelectionModel::SetSrcAndDestRectFromPoints(const POINT& ptFrom, const POINT& ptTo)
243 {
244     m_rcDest.left = m_rcSrc.left = min(ptFrom.x, ptTo.x);
245     m_rcDest.top = m_rcSrc.top = min(ptFrom.y, ptTo.y);
246     m_rcDest.right = m_rcSrc.right = max(ptFrom.x, ptTo.x);
247     m_rcDest.bottom = m_rcSrc.bottom = max(ptFrom.y, ptTo.y);
248 }
249 
250 void SelectionModel::SetSrcRectSizeToZero()
251 {
252     m_rcSrc.right = m_rcSrc.left;
253     m_rcSrc.bottom = m_rcSrc.top;
254 }
255 
256 BOOL SelectionModel::IsSrcRectSizeNonzero()
257 {
258     return (RECT_WIDTH(m_rcSrc) != 0) && (RECT_HEIGHT(m_rcSrc) != 0);
259 }
260 
261 void SelectionModel::ModifyDestRect(POINT& ptDelta, int iAction)
262 {
263     POINT ptDeltaUsed;
264 
265     switch (iAction)
266     {
267         case ACTION_MOVE:                /* move selection */
268             ptDeltaUsed.x = ptDelta.x;
269             ptDeltaUsed.y = ptDelta.y;
270             OffsetRect(&m_rcDest, ptDeltaUsed.x, ptDeltaUsed.y);
271             break;
272         case ACTION_RESIZE_TOP_LEFT:     /* resize at upper left corner */
273             ptDeltaUsed.x = min(ptDelta.x, RECT_WIDTH(m_rcDest) - 1);
274             ptDeltaUsed.y = min(ptDelta.y, RECT_HEIGHT(m_rcDest) - 1);
275             m_rcDest.left += ptDeltaUsed.x;
276             m_rcDest.top  += ptDeltaUsed.y;
277             break;
278         case ACTION_RESIZE_TOP:          /* resize at top edge */
279             ptDeltaUsed.x = ptDelta.x;
280             ptDeltaUsed.y = min(ptDelta.y, RECT_HEIGHT(m_rcDest) - 1);
281             m_rcDest.top += ptDeltaUsed.y;
282             break;
283         case ACTION_RESIZE_TOP_RIGHT:    /* resize at upper right corner */
284             ptDeltaUsed.x = max(ptDelta.x, -(RECT_WIDTH(m_rcDest) - 1));
285             ptDeltaUsed.y = min(ptDelta.y, RECT_HEIGHT(m_rcDest) - 1);
286             m_rcDest.top   += ptDeltaUsed.y;
287             m_rcDest.right += ptDeltaUsed.x;
288             break;
289         case ACTION_RESIZE_LEFT:         /* resize at left edge */
290             ptDeltaUsed.x = min(ptDelta.x, RECT_WIDTH(m_rcDest) - 1);
291             ptDeltaUsed.y = ptDelta.y;
292             m_rcDest.left += ptDeltaUsed.x;
293             break;
294         case ACTION_RESIZE_RIGHT:        /* resize at right edge */
295             ptDeltaUsed.x = max(ptDelta.x, -(RECT_WIDTH(m_rcDest) - 1));
296             ptDeltaUsed.y = ptDelta.y;
297             m_rcDest.right += ptDeltaUsed.x;
298             break;
299         case ACTION_RESIZE_BOTTOM_LEFT:  /* resize at lower left corner */
300             ptDeltaUsed.x = min(ptDelta.x, RECT_WIDTH(m_rcDest) - 1);
301             ptDeltaUsed.y = max(ptDelta.y, -(RECT_HEIGHT(m_rcDest) - 1));
302             m_rcDest.left   += ptDeltaUsed.x;
303             m_rcDest.bottom += ptDeltaUsed.y;
304             break;
305         case ACTION_RESIZE_BOTTOM:       /* resize at bottom edge */
306             ptDeltaUsed.x = ptDelta.x;
307             ptDeltaUsed.y = max(ptDelta.y, -(RECT_HEIGHT(m_rcDest) - 1));
308             m_rcDest.bottom += ptDeltaUsed.y;
309             break;
310         case ACTION_RESIZE_BOTTOM_RIGHT: /* resize at lower right corner */
311             ptDeltaUsed.x = max(ptDelta.x, -(RECT_WIDTH(m_rcDest) - 1));
312             ptDeltaUsed.y = max(ptDelta.y, -(RECT_HEIGHT(m_rcDest) - 1));
313             m_rcDest.right  += ptDeltaUsed.x;
314             m_rcDest.bottom += ptDeltaUsed.y;
315             break;
316     }
317     ptDelta.x -= ptDeltaUsed.x;
318     ptDelta.y -= ptDeltaUsed.y;
319 }
320 
321 LONG SelectionModel::GetDestRectWidth()
322 {
323     return m_rcDest.right - m_rcDest.left;
324 }
325 
326 LONG SelectionModel::GetDestRectHeight()
327 {
328     return m_rcDest.bottom - m_rcDest.top;
329 }
330 
331 LONG SelectionModel::GetDestRectLeft()
332 {
333     return m_rcDest.left;
334 }
335 
336 LONG SelectionModel::GetDestRectTop()
337 {
338     return m_rcDest.top;
339 }
340 
341 void SelectionModel::DrawTextToolText(HDC hDCImage, COLORREF crFg, COLORREF crBg, BOOL bBgTransparent)
342 {
343     Text(hDCImage, m_rcDest.left, m_rcDest.top, m_rcDest.right, m_rcDest.bottom, crFg, crBg, textToolText, hfontTextFont, bBgTransparent);
344 }
345 
346 void SelectionModel::NotifyRefreshNeeded()
347 {
348     selectionWindow.SendMessage(WM_SELECTIONMODELREFRESHNEEDED);
349 }
350