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