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