xref: /reactos/base/applications/mspaint/dib.cpp (revision 9afcbea2)
1 /*
2  * PROJECT:     PAINT for ReactOS
3  * LICENSE:     LGPL
4  * FILE:        base/applications/mspaint/dib.cpp
5  * PURPOSE:     Some DIB related functions
6  * PROGRAMMERS: Benedikt Freisen
7  */
8 
9 #include "precomp.h"
10 #include <math.h>
11 
12 INT g_fileSize = 0;
13 float g_xDpi = 96;
14 float g_yDpi = 96;
15 SYSTEMTIME g_fileTime;
16 
17 /* FUNCTIONS ********************************************************/
18 
19 // Convert DPI (dots per inch) into PPCM (pixels per centimeter)
20 float PpcmFromDpi(float dpi)
21 {
22     // 1 DPI is 0.0254 meter. 1 centimeter is 1/100 meter.
23     return dpi / (0.0254f * 100.0f);
24 }
25 
26 HBITMAP
27 CreateDIBWithProperties(int width, int height)
28 {
29     BITMAPINFO bmi;
30     ZeroMemory(&bmi, sizeof(BITMAPINFO));
31     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
32     bmi.bmiHeader.biWidth = width;
33     bmi.bmiHeader.biHeight = height;
34     bmi.bmiHeader.biPlanes = 1;
35     bmi.bmiHeader.biBitCount = 24;
36     return CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
37 }
38 
39 HBITMAP
40 CreateMonoBitmap(int width, int height, BOOL bWhite)
41 {
42     HBITMAP hbm = CreateBitmap(width, height, 1, 1, NULL);
43     if (hbm == NULL)
44         return NULL;
45 
46     if (bWhite)
47     {
48         HDC hdc = CreateCompatibleDC(NULL);
49         HGDIOBJ hbmOld = SelectObject(hdc, hbm);
50         RECT rc = { 0, 0, width, height };
51         FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
52         SelectObject(hdc, hbmOld);
53         DeleteDC(hdc);
54     }
55 
56     return hbm;
57 }
58 
59 HBITMAP
60 CreateColorDIB(int width, int height, COLORREF rgb)
61 {
62     HBITMAP ret = CreateDIBWithProperties(width, height);
63     if (!ret)
64         return NULL;
65 
66     if (rgb)
67     {
68         HDC hdc = CreateCompatibleDC(NULL);
69         HGDIOBJ hbmOld = SelectObject(hdc, ret);
70         RECT rc;
71         SetRect(&rc, 0, 0, width, height);
72         HBRUSH hbr = CreateSolidBrush(rgb);
73         FillRect(hdc, &rc, hbr);
74         DeleteObject(hbr);
75         SelectObject(hdc, hbmOld);
76         DeleteDC(hdc);
77     }
78 
79     return ret;
80 }
81 
82 HBITMAP CopyMonoImage(HBITMAP hbm, INT cx, INT cy)
83 {
84     BITMAP bm;
85     if (!GetObject(hbm, sizeof(bm), &bm))
86         return NULL;
87 
88     if (cx == 0 || cy == 0)
89     {
90         cx = bm.bmWidth;
91         cy = bm.bmHeight;
92     }
93 
94     HBITMAP hbmNew = CreateBitmap(cx, cy, 1, 1, NULL);
95     if (!hbmNew)
96         return NULL;
97 
98     HDC hdc1 = CreateCompatibleDC(NULL);
99     HDC hdc2 = CreateCompatibleDC(NULL);
100     HGDIOBJ hbm1Old = SelectObject(hdc1, hbm);
101     HGDIOBJ hbm2Old = SelectObject(hdc2, hbmNew);
102     StretchBlt(hdc2, 0, 0, cx, cy, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
103     SelectObject(hdc1, hbm1Old);
104     SelectObject(hdc1, hbm2Old);
105     DeleteDC(hdc1);
106     DeleteDC(hdc2);
107     return hbmNew;
108 }
109 
110 HBITMAP CachedBufferDIB(HBITMAP hbm, int minimalWidth, int minimalHeight)
111 {
112     if (minimalWidth <= 0)
113         minimalWidth = 1;
114     if (minimalHeight <= 0)
115         minimalHeight = 1;
116 
117     BITMAP bm;
118     if (!GetObject(hbm, sizeof(bm), &bm))
119         hbm = NULL;
120 
121     if (hbm && minimalWidth <= bm.bmWidth && minimalHeight <= bm.bmHeight)
122         return hbm;
123 
124     if (hbm)
125         DeleteObject(hbm);
126 
127     return CreateDIBWithProperties((minimalWidth * 3) / 2, (minimalHeight * 3) / 2);
128 }
129 
130 int
131 GetDIBWidth(HBITMAP hBitmap)
132 {
133     BITMAP bm;
134     GetObject(hBitmap, sizeof(BITMAP), &bm);
135     return bm.bmWidth;
136 }
137 
138 int
139 GetDIBHeight(HBITMAP hBitmap)
140 {
141     BITMAP bm;
142     GetObject(hBitmap, sizeof(BITMAP), &bm);
143     return bm.bmHeight;
144 }
145 
146 BOOL SaveDIBToFile(HBITMAP hBitmap, LPCTSTR FileName, HDC hDC)
147 {
148     CImageDx img;
149     img.Attach(hBitmap);
150     img.SaveDx(FileName, GUID_NULL, g_xDpi, g_yDpi); // TODO: error handling
151     img.Detach();
152 
153     WIN32_FIND_DATA find;
154     HANDLE hFind = FindFirstFile(FileName, &find);
155     if (hFind == INVALID_HANDLE_VALUE)
156     {
157         ShowFileLoadError(FileName);
158         return FALSE;
159     }
160     FindClose(hFind);
161 
162     // update time and size
163     FILETIME ft;
164     FileTimeToLocalFileTime(&find.ftLastWriteTime, &ft);
165     FileTimeToSystemTime(&ft, &g_fileTime);
166     g_fileSize = find.nFileSizeLow;
167 
168     // TODO: update hRes and vRes
169 
170     registrySettings.SetMostRecentFile(FileName);
171 
172     g_isAFile = TRUE;
173     g_imageSaved = TRUE;
174     return TRUE;
175 }
176 
177 void ShowFileLoadError(LPCTSTR name)
178 {
179     CString strText;
180     strText.Format(IDS_LOADERRORTEXT, (LPCTSTR) name);
181     CString strProgramName;
182     strProgramName.LoadString(IDS_PROGRAMNAME);
183     mainWindow.MessageBox(strText, strProgramName, MB_OK | MB_ICONEXCLAMATION);
184 }
185 
186 HBITMAP SetBitmapAndInfo(HBITMAP hBitmap, LPCTSTR name, DWORD dwFileSize, BOOL isFile)
187 {
188     if (hBitmap == NULL)
189     {
190         COLORREF white = RGB(255, 255, 255);
191         hBitmap = CreateColorDIB(registrySettings.BMPWidth,
192                                  registrySettings.BMPHeight, white);
193         if (hBitmap == NULL)
194             return FALSE;
195 
196         HDC hScreenDC = GetDC(NULL);
197         g_xDpi = GetDeviceCaps(hScreenDC, LOGPIXELSX);
198         g_yDpi = GetDeviceCaps(hScreenDC, LOGPIXELSY);
199         ReleaseDC(NULL, hScreenDC);
200 
201         ZeroMemory(&g_fileTime, sizeof(g_fileTime));
202     }
203 
204     // update image
205     imageModel.PushImageForUndo(hBitmap);
206     imageModel.ClearHistory();
207 
208     // update g_fileSize
209     g_fileSize = dwFileSize;
210 
211     // update g_szFileName
212     if (name && name[0])
213         GetFullPathName(name, _countof(g_szFileName), g_szFileName, NULL);
214     else
215         LoadString(g_hinstExe, IDS_DEFAULTFILENAME, g_szFileName, _countof(g_szFileName));
216 
217     // set title
218     CString strTitle;
219     strTitle.Format(IDS_WINDOWTITLE, PathFindFileName(g_szFileName));
220     mainWindow.SetWindowText(strTitle);
221 
222     // update file info and recent
223     g_isAFile = isFile;
224     if (g_isAFile)
225         registrySettings.SetMostRecentFile(g_szFileName);
226 
227     g_imageSaved = TRUE;
228 
229     return hBitmap;
230 }
231 
232 HBITMAP DoLoadImageFile(HWND hwnd, LPCTSTR name, BOOL fIsMainFile)
233 {
234     // find the file
235     WIN32_FIND_DATA find;
236     HANDLE hFind = FindFirstFile(name, &find);
237     if (hFind == INVALID_HANDLE_VALUE)
238     {
239         // does not exist
240         CStringW strText;
241         strText.Format(IDS_LOADERRORTEXT, name);
242         MessageBoxW(hwnd, strText, NULL, MB_ICONERROR);
243         return NULL;
244     }
245     DWORD dwFileSize = find.nFileSizeLow; // get file size
246     FindClose(hFind);
247 
248     // is file empty?
249     if (dwFileSize == 0)
250     {
251         if (fIsMainFile)
252         {
253             FILETIME ft;
254             FileTimeToLocalFileTime(&find.ftLastWriteTime, &ft);
255             FileTimeToSystemTime(&ft, &g_fileTime);
256             return SetBitmapAndInfo(NULL, name, dwFileSize, TRUE);
257         }
258     }
259 
260     // load the image
261     CImageDx img;
262     img.LoadDx(name, &g_xDpi, &g_yDpi);
263 
264     if (g_xDpi <= 0)
265         g_xDpi = 96;
266     if (g_yDpi <= 0)
267         g_yDpi = 96;
268 
269     HBITMAP hBitmap = img.Detach();
270 
271     if (hBitmap == NULL)
272     {
273         // cannot open
274         CStringW strText;
275         strText.Format(IDS_LOADERRORTEXT, name);
276         MessageBoxW(hwnd, strText, NULL, MB_ICONERROR);
277         return NULL;
278     }
279 
280     if (fIsMainFile)
281     {
282         FILETIME ft;
283         FileTimeToLocalFileTime(&find.ftLastWriteTime, &ft);
284         FileTimeToSystemTime(&ft, &g_fileTime);
285         SetBitmapAndInfo(hBitmap, name, dwFileSize, TRUE);
286     }
287 
288     return hBitmap;
289 }
290 
291 HBITMAP Rotate90DegreeBlt(HDC hDC1, INT cx, INT cy, BOOL bRight, BOOL bMono)
292 {
293     HBITMAP hbm2;
294     if (bMono)
295         hbm2 = ::CreateBitmap(cy, cx, 1, 1, NULL);
296     else
297         hbm2 = CreateDIBWithProperties(cy, cx);
298     if (!hbm2)
299         return NULL;
300 
301     HDC hDC2 = CreateCompatibleDC(NULL);
302     HGDIOBJ hbm2Old = SelectObject(hDC2, hbm2);
303     if (bRight)
304     {
305         for (INT y = 0; y < cy; ++y)
306         {
307             for (INT x = 0; x < cx; ++x)
308             {
309                 COLORREF rgb = GetPixel(hDC1, x, y);
310                 SetPixelV(hDC2, cy - (y + 1), x, rgb);
311             }
312         }
313     }
314     else
315     {
316         for (INT y = 0; y < cy; ++y)
317         {
318             for (INT x = 0; x < cx; ++x)
319             {
320                 COLORREF rgb = GetPixel(hDC1, x, y);
321                 SetPixelV(hDC2, y, cx - (x + 1), rgb);
322             }
323         }
324     }
325     SelectObject(hDC2, hbm2Old);
326     DeleteDC(hDC2);
327     return hbm2;
328 }
329 
330 #ifndef M_PI
331     #define M_PI 3.14159265
332 #endif
333 
334 HBITMAP SkewDIB(HDC hDC1, HBITMAP hbm, INT nDegree, BOOL bVertical, BOOL bMono)
335 {
336     if (nDegree == 0)
337         return CopyDIBImage(hbm);
338 
339     const double eTan = tan(abs(nDegree) * M_PI / 180);
340 
341     BITMAP bm;
342     GetObjectW(hbm, sizeof(bm), &bm);
343     INT cx = bm.bmWidth, cy = bm.bmHeight, dx = 0, dy = 0;
344     if (bVertical)
345         dy = INT(cx * eTan);
346     else
347         dx = INT(cy * eTan);
348 
349     if (dx == 0 && dy == 0)
350         return CopyDIBImage(hbm);
351 
352     HBITMAP hbmNew;
353     if (bMono)
354         hbmNew = CreateMonoBitmap(cx + dx, cy + dy, FALSE);
355     else
356         hbmNew = CreateColorDIB(cx + dx, cy + dy, RGB(255, 255, 255));
357     if (!hbmNew)
358         return NULL;
359 
360     HDC hDC2 = CreateCompatibleDC(NULL);
361     HGDIOBJ hbm2Old = SelectObject(hDC2, hbmNew);
362     if (bVertical)
363     {
364         for (INT x = 0; x < cx; ++x)
365         {
366             INT delta = INT(x * eTan);
367             if (nDegree > 0)
368                 BitBlt(hDC2, x, (dy - delta), 1, cy, hDC1, x, 0, SRCCOPY);
369             else
370                 BitBlt(hDC2, x, delta, 1, cy, hDC1, x, 0, SRCCOPY);
371         }
372     }
373     else
374     {
375         for (INT y = 0; y < cy; ++y)
376         {
377             INT delta = INT(y * eTan);
378             if (nDegree > 0)
379                 BitBlt(hDC2, (dx - delta), y, cx, 1, hDC1, 0, y, SRCCOPY);
380             else
381                 BitBlt(hDC2, delta, y, cx, 1, hDC1, 0, y, SRCCOPY);
382         }
383     }
384 
385     SelectObject(hDC2, hbm2Old);
386     DeleteDC(hDC2);
387     return hbmNew;
388 }
389 
390 struct BITMAPINFODX : BITMAPINFO
391 {
392     RGBQUAD bmiColorsAdditional[256 - 1];
393 };
394 
395 HGLOBAL BitmapToClipboardDIB(HBITMAP hBitmap)
396 {
397     BITMAP bm;
398     if (!GetObject(hBitmap, sizeof(BITMAP), &bm))
399         return NULL;
400 
401     BITMAPINFODX bmi;
402     ZeroMemory(&bmi, sizeof(bmi));
403     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
404     bmi.bmiHeader.biWidth = bm.bmWidth;
405     bmi.bmiHeader.biHeight = bm.bmHeight;
406     bmi.bmiHeader.biPlanes = 1;
407     bmi.bmiHeader.biBitCount = bm.bmBitsPixel;
408     bmi.bmiHeader.biCompression = BI_RGB;
409     bmi.bmiHeader.biSizeImage = bm.bmWidthBytes * bm.bmHeight;
410 
411     INT cColors;
412     if (bm.bmBitsPixel < 16)
413         cColors = 1 << bm.bmBitsPixel;
414     else
415         cColors = 0;
416 
417     HDC hDC = CreateCompatibleDC(NULL);
418 
419     if (cColors)
420     {
421         HGDIOBJ hbmOld = SelectObject(hDC, hBitmap);
422         cColors = GetDIBColorTable(hDC, 0, cColors, bmi.bmiColors);
423         SelectObject(hDC, hbmOld);
424     }
425 
426     DWORD cbColors = cColors * sizeof(RGBQUAD);
427     DWORD dwSize = sizeof(BITMAPINFOHEADER) + cbColors + bmi.bmiHeader.biSizeImage;
428     HGLOBAL hGlobal = GlobalAlloc(GHND | GMEM_SHARE, dwSize);
429     if (hGlobal)
430     {
431         LPBYTE pb = (LPBYTE)GlobalLock(hGlobal);
432         if (pb)
433         {
434             CopyMemory(pb, &bmi, sizeof(BITMAPINFOHEADER));
435             pb += sizeof(BITMAPINFOHEADER);
436 
437             CopyMemory(pb, bmi.bmiColors, cbColors);
438             pb += cbColors;
439 
440             GetDIBits(hDC, hBitmap, 0, bm.bmHeight, pb, &bmi, DIB_RGB_COLORS);
441 
442             GlobalUnlock(hGlobal);
443         }
444         else
445         {
446             GlobalFree(hGlobal);
447             hGlobal = NULL;
448         }
449     }
450 
451     DeleteDC(hDC);
452 
453     return hGlobal;
454 }
455 
456 HBITMAP BitmapFromClipboardDIB(HGLOBAL hGlobal)
457 {
458     LPBYTE pb = (LPBYTE)GlobalLock(hGlobal);
459     if (!pb)
460         return NULL;
461 
462     LPBITMAPINFO pbmi = (LPBITMAPINFO)pb;
463     pb += pbmi->bmiHeader.biSize;
464 
465     INT cColors = 0, cbColors = 0;
466     if (pbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
467     {
468         LPBITMAPCOREINFO pbmci = (LPBITMAPCOREINFO)pbmi;
469         WORD BitCount = pbmci->bmciHeader.bcBitCount;
470         if (BitCount < 16)
471         {
472             cColors = (1 << BitCount);
473             cbColors = cColors * sizeof(RGBTRIPLE);
474             pb += cbColors;
475         }
476     }
477     else if (pbmi->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER))
478     {
479         WORD BitCount = pbmi->bmiHeader.biBitCount;
480         if (BitCount < 16)
481         {
482             cColors = (1 << BitCount);
483             cbColors = cColors * sizeof(RGBQUAD);
484             pb += cbColors;
485         }
486     }
487 
488     HDC hDC = CreateCompatibleDC(NULL);
489     HBITMAP hBitmap = CreateDIBSection(hDC, pbmi, DIB_RGB_COLORS, NULL, NULL, 0);
490     if (hBitmap)
491     {
492         SetDIBits(hDC, hBitmap, 0, labs(pbmi->bmiHeader.biHeight), pb, pbmi, DIB_RGB_COLORS);
493     }
494     DeleteDC(hDC);
495 
496     GlobalUnlock(hGlobal);
497 
498     return hBitmap;
499 }
500 
501 HBITMAP BitmapFromHEMF(HENHMETAFILE hEMF)
502 {
503     ENHMETAHEADER header;
504     if (!GetEnhMetaFileHeader(hEMF, sizeof(header), &header))
505         return NULL;
506 
507     CRect rc = *(LPRECT)&header.rclBounds;
508     INT cx = rc.Width(), cy = rc.Height();
509     HBITMAP hbm = CreateColorDIB(cx, cy, RGB(255, 255, 255));
510     if (!hbm)
511         return NULL;
512 
513     HDC hDC = CreateCompatibleDC(NULL);
514     HGDIOBJ hbmOld = SelectObject(hDC, hbm);
515     PlayEnhMetaFile(hDC, hEMF, &rc);
516     SelectObject(hDC, hbmOld);
517     DeleteDC(hDC);
518 
519     return hbm;
520 }
521