xref: /reactos/base/applications/mspaint/dib.cpp (revision 764e5505)
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 
10 INT g_fileSize = 0;
11 float g_xDpi = 96;
12 float g_yDpi = 96;
13 SYSTEMTIME g_fileTime;
14 
15 #define WIDTHBYTES(i) (((i) + 31) / 32 * 4)
16 
17 struct BITMAPINFOEX : BITMAPINFO
18 {
19     RGBQUAD bmiColorsExtra[256 - 1];
20 };
21 
22 /* FUNCTIONS ********************************************************/
23 
24 // Convert DPI (dots per inch) into PPCM (pixels per centimeter)
PpcmFromDpi(float dpi)25 float PpcmFromDpi(float dpi)
26 {
27     // 1 DPI is 0.0254 meter. 1 centimeter is 1/100 meter.
28     return dpi / (0.0254f * 100.0f);
29 }
30 
31 HBITMAP
CreateDIBWithProperties(int width,int height)32 CreateDIBWithProperties(int width, int height)
33 {
34     BITMAPINFO bmi;
35     ZeroMemory(&bmi, sizeof(BITMAPINFO));
36     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
37     bmi.bmiHeader.biWidth = width;
38     bmi.bmiHeader.biHeight = height;
39     bmi.bmiHeader.biPlanes = 1;
40     bmi.bmiHeader.biBitCount = 24;
41     return CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
42 }
43 
44 HBITMAP
CreateMonoBitmap(int width,int height,BOOL bWhite)45 CreateMonoBitmap(int width, int height, BOOL bWhite)
46 {
47     HBITMAP hbm = CreateBitmap(width, height, 1, 1, NULL);
48     if (hbm == NULL)
49         return NULL;
50 
51     if (bWhite)
52     {
53         HDC hdc = CreateCompatibleDC(NULL);
54         HGDIOBJ hbmOld = SelectObject(hdc, hbm);
55         RECT rc = { 0, 0, width, height };
56         FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
57         SelectObject(hdc, hbmOld);
58         DeleteDC(hdc);
59     }
60 
61     return hbm;
62 }
63 
64 HBITMAP
CreateColorDIB(int width,int height,COLORREF rgb)65 CreateColorDIB(int width, int height, COLORREF rgb)
66 {
67     HBITMAP ret = CreateDIBWithProperties(width, height);
68     if (!ret)
69         return NULL;
70 
71     if (rgb)
72     {
73         HDC hdc = CreateCompatibleDC(NULL);
74         HGDIOBJ hbmOld = SelectObject(hdc, ret);
75         RECT rc;
76         SetRect(&rc, 0, 0, width, height);
77         HBRUSH hbr = CreateSolidBrush(rgb);
78         FillRect(hdc, &rc, hbr);
79         DeleteObject(hbr);
80         SelectObject(hdc, hbmOld);
81         DeleteDC(hdc);
82     }
83 
84     return ret;
85 }
86 
CopyMonoImage(HBITMAP hbm,INT cx,INT cy)87 HBITMAP CopyMonoImage(HBITMAP hbm, INT cx, INT cy)
88 {
89     BITMAP bm;
90     if (!::GetObjectW(hbm, sizeof(bm), &bm))
91         return NULL;
92 
93     if (cx == 0 || cy == 0)
94     {
95         cx = bm.bmWidth;
96         cy = bm.bmHeight;
97     }
98 
99     HBITMAP hbmNew = ::CreateBitmap(cx, cy, 1, 1, NULL);
100     if (!hbmNew)
101         return NULL;
102 
103     HDC hdc1 = ::CreateCompatibleDC(NULL);
104     HDC hdc2 = ::CreateCompatibleDC(NULL);
105     HGDIOBJ hbm1Old = ::SelectObject(hdc1, hbm);
106     HGDIOBJ hbm2Old = ::SelectObject(hdc2, hbmNew);
107     ::StretchBlt(hdc2, 0, 0, cx, cy, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
108     ::SelectObject(hdc1, hbm1Old);
109     ::SelectObject(hdc2, hbm2Old);
110     ::DeleteDC(hdc1);
111     ::DeleteDC(hdc2);
112     return hbmNew;
113 }
114 
CachedBufferDIB(HBITMAP hbm,int minimalWidth,int minimalHeight)115 HBITMAP CachedBufferDIB(HBITMAP hbm, int minimalWidth, int minimalHeight)
116 {
117     if (minimalWidth <= 0)
118         minimalWidth = 1;
119     if (minimalHeight <= 0)
120         minimalHeight = 1;
121 
122     BITMAP bm;
123     if (!GetObjectW(hbm, sizeof(bm), &bm))
124         hbm = NULL;
125 
126     if (hbm && minimalWidth <= bm.bmWidth && minimalHeight <= bm.bmHeight)
127         return hbm;
128 
129     if (hbm)
130         DeleteObject(hbm);
131 
132     return CreateDIBWithProperties((minimalWidth * 3) / 2, (minimalHeight * 3) / 2);
133 }
134 
135 int
GetDIBWidth(HBITMAP hBitmap)136 GetDIBWidth(HBITMAP hBitmap)
137 {
138     BITMAP bm;
139     ::GetObjectW(hBitmap, sizeof(BITMAP), &bm);
140     return bm.bmWidth;
141 }
142 
143 int
GetDIBHeight(HBITMAP hBitmap)144 GetDIBHeight(HBITMAP hBitmap)
145 {
146     BITMAP bm;
147     ::GetObjectW(hBitmap, sizeof(BITMAP), &bm);
148     return bm.bmHeight;
149 }
150 
SaveDIBToFile(HBITMAP hBitmap,LPCWSTR FileName,BOOL fIsMainFile,REFGUID guidFileType)151 BOOL SaveDIBToFile(HBITMAP hBitmap, LPCWSTR FileName, BOOL fIsMainFile, REFGUID guidFileType)
152 {
153     CWaitCursor waitCursor;
154 
155     CImageDx img;
156     img.Attach(hBitmap);
157     HRESULT hr = img.SaveDx(FileName, guidFileType, g_xDpi, g_yDpi);
158     img.Detach();
159 
160     if (FAILED(hr))
161     {
162         ShowError(IDS_SAVEERROR, FileName);
163         return FALSE;
164     }
165 
166     if (!fIsMainFile)
167         return TRUE;
168 
169     WIN32_FIND_DATAW find;
170     HANDLE hFind = ::FindFirstFileW(FileName, &find);
171     if (hFind == INVALID_HANDLE_VALUE)
172     {
173         ShowError(IDS_SAVEERROR, FileName);
174         return FALSE;
175     }
176     ::FindClose(hFind);
177 
178     SetFileInfo(FileName, &find, TRUE);
179     g_imageSaved = TRUE;
180     return TRUE;
181 }
182 
SetFileInfo(LPCWSTR name,LPWIN32_FIND_DATAW pFound,BOOL isAFile)183 void SetFileInfo(LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isAFile)
184 {
185     // update file time and size
186     if (pFound)
187     {
188         FILETIME ft;
189         ::FileTimeToLocalFileTime(&pFound->ftLastWriteTime, &ft);
190         ::FileTimeToSystemTime(&ft, &g_fileTime);
191 
192         g_fileSize = pFound->nFileSizeLow;
193     }
194     else
195     {
196         ZeroMemory(&g_fileTime, sizeof(g_fileTime));
197         g_fileSize = 0;
198     }
199 
200     // update g_szFileName
201     if (name && name[0])
202     {
203         CStringW strName = name;
204         ::GetFullPathNameW(strName, _countof(g_szFileName), g_szFileName, NULL);
205         // The following code won't work correctly when (name == g_szFileName):
206         //   ::GetFullPathNameW(name, _countof(g_szFileName), g_szFileName, NULL);
207     }
208     else
209     {
210         ::LoadStringW(g_hinstExe, IDS_DEFAULTFILENAME, g_szFileName, _countof(g_szFileName));
211     }
212 
213     // set title
214     CStringW strTitle;
215     strTitle.Format(IDS_WINDOWTITLE, PathFindFileNameW(g_szFileName));
216     mainWindow.SetWindowText(strTitle);
217 
218     // update file info and recent
219     g_isAFile = isAFile;
220     if (g_isAFile)
221         registrySettings.SetMostRecentFile(g_szFileName);
222 
223     g_imageSaved = TRUE;
224 }
225 
InitializeImage(LPCWSTR name,LPWIN32_FIND_DATAW pFound,BOOL isFile)226 HBITMAP InitializeImage(LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile)
227 {
228     COLORREF white = RGB(255, 255, 255);
229     HBITMAP hBitmap = CreateColorDIB(registrySettings.BMPWidth, registrySettings.BMPHeight, white);
230     if (hBitmap == NULL)
231     {
232         ShowOutOfMemory();
233         return NULL;
234     }
235 
236     HDC hScreenDC = ::GetDC(NULL);
237     g_xDpi = (float)::GetDeviceCaps(hScreenDC, LOGPIXELSX);
238     g_yDpi = (float)::GetDeviceCaps(hScreenDC, LOGPIXELSY);
239     ::ReleaseDC(NULL, hScreenDC);
240 
241     return SetBitmapAndInfo(hBitmap, name, pFound, isFile);
242 }
243 
SetBitmapAndInfo(HBITMAP hBitmap,LPCWSTR name,LPWIN32_FIND_DATAW pFound,BOOL isFile)244 HBITMAP SetBitmapAndInfo(HBITMAP hBitmap, LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile)
245 {
246     // update image
247     canvasWindow.updateScrollPos();
248     imageModel.PushImageForUndo(hBitmap);
249     imageModel.ClearHistory();
250 
251     SetFileInfo(name, pFound, isFile);
252     g_imageSaved = TRUE;
253     return hBitmap;
254 }
255 
DoLoadImageFile(HWND hwnd,LPCWSTR name,BOOL fIsMainFile)256 HBITMAP DoLoadImageFile(HWND hwnd, LPCWSTR name, BOOL fIsMainFile)
257 {
258     CWaitCursor waitCursor;
259 
260     // find the file
261     WIN32_FIND_DATAW find;
262     HANDLE hFind = ::FindFirstFileW(name, &find);
263     if (hFind == INVALID_HANDLE_VALUE) // does not exist
264     {
265         ShowError(IDS_LOADERRORTEXT, name);
266         return NULL;
267     }
268     ::FindClose(hFind);
269 
270     // is file empty?
271     if (find.nFileSizeLow == 0 && find.nFileSizeHigh == 0)
272     {
273         if (fIsMainFile)
274             return InitializeImage(name, &find, TRUE);
275     }
276 
277     // load the image
278     CImageDx img;
279     float xDpi = 0, yDpi = 0;
280     HRESULT hr = img.LoadDx(name, &xDpi, &yDpi);
281     if (FAILED(hr) && fIsMainFile)
282     {
283         imageModel.ClearHistory();
284         hr = img.LoadDx(name, &xDpi, &yDpi);
285     }
286     if (FAILED(hr))
287     {
288         ATLTRACE("hr: 0x%08lX\n", hr);
289         ShowError(IDS_LOADERRORTEXT, name);
290         return NULL;
291     }
292 
293     HBITMAP hBitmap = img.Detach();
294     if (!fIsMainFile)
295         return hBitmap;
296 
297     if (xDpi <= 0 || yDpi <= 0)
298     {
299         HDC hDC = ::GetDC(NULL);
300         xDpi = (float)::GetDeviceCaps(hDC, LOGPIXELSX);
301         yDpi = (float)::GetDeviceCaps(hDC, LOGPIXELSY);
302         ::ReleaseDC(NULL, hDC);
303     }
304 
305     g_xDpi = xDpi;
306     g_yDpi = yDpi;
307 
308     SetBitmapAndInfo(hBitmap, name, &find, TRUE);
309     return hBitmap;
310 }
311 
Rotate90DegreeBlt(HDC hDC1,INT cx,INT cy,BOOL bRight,BOOL bMono)312 HBITMAP Rotate90DegreeBlt(HDC hDC1, INT cx, INT cy, BOOL bRight, BOOL bMono)
313 {
314     HBITMAP hbm2;
315     if (bMono)
316         hbm2 = ::CreateBitmap(cy, cx, 1, 1, NULL);
317     else
318         hbm2 = CreateDIBWithProperties(cy, cx);
319     if (!hbm2)
320         return NULL;
321 
322     HDC hDC2 = CreateCompatibleDC(NULL);
323     HGDIOBJ hbm2Old = SelectObject(hDC2, hbm2);
324     if (bRight)
325     {
326         for (INT y = 0; y < cy; ++y)
327         {
328             for (INT x = 0; x < cx; ++x)
329             {
330                 COLORREF rgb = GetPixel(hDC1, x, y);
331                 SetPixelV(hDC2, cy - (y + 1), x, rgb);
332             }
333         }
334     }
335     else
336     {
337         for (INT y = 0; y < cy; ++y)
338         {
339             for (INT x = 0; x < cx; ++x)
340             {
341                 COLORREF rgb = GetPixel(hDC1, x, y);
342                 SetPixelV(hDC2, y, cx - (x + 1), rgb);
343             }
344         }
345     }
346     SelectObject(hDC2, hbm2Old);
347     DeleteDC(hDC2);
348     return hbm2;
349 }
350 
SkewDIB(HDC hDC1,HBITMAP hbm,INT nDegree,BOOL bVertical,BOOL bMono)351 HBITMAP SkewDIB(HDC hDC1, HBITMAP hbm, INT nDegree, BOOL bVertical, BOOL bMono)
352 {
353     CWaitCursor waitCursor;
354 
355     if (nDegree == 0)
356         return CopyDIBImage(hbm);
357 
358     const double eTan = tan(abs(nDegree) * M_PI / 180);
359 
360     BITMAP bm;
361     ::GetObjectW(hbm, sizeof(bm), &bm);
362     INT cx = bm.bmWidth, cy = bm.bmHeight, dx = 0, dy = 0;
363     if (bVertical)
364         dy = INT(cx * eTan);
365     else
366         dx = INT(cy * eTan);
367 
368     if (dx == 0 && dy == 0)
369         return CopyDIBImage(hbm);
370 
371     HBITMAP hbmNew;
372     if (bMono)
373         hbmNew = CreateMonoBitmap(cx + dx, cy + dy, FALSE);
374     else
375         hbmNew = CreateColorDIB(cx + dx, cy + dy, RGB(255, 255, 255));
376     if (!hbmNew)
377         return NULL;
378 
379     HDC hDC2 = CreateCompatibleDC(NULL);
380     HGDIOBJ hbm2Old = SelectObject(hDC2, hbmNew);
381     if (bVertical)
382     {
383         for (INT x = 0; x < cx; ++x)
384         {
385             INT delta = INT(x * eTan);
386             if (nDegree > 0)
387                 ::BitBlt(hDC2, x, (dy - delta), 1, cy, hDC1, x, 0, SRCCOPY);
388             else
389                 ::BitBlt(hDC2, x, delta, 1, cy, hDC1, x, 0, SRCCOPY);
390         }
391     }
392     else
393     {
394         for (INT y = 0; y < cy; ++y)
395         {
396             INT delta = INT(y * eTan);
397             if (nDegree > 0)
398                 ::BitBlt(hDC2, (dx - delta), y, cx, 1, hDC1, 0, y, SRCCOPY);
399             else
400                 ::BitBlt(hDC2, delta, y, cx, 1, hDC1, 0, y, SRCCOPY);
401         }
402     }
403 
404     SelectObject(hDC2, hbm2Old);
405     DeleteDC(hDC2);
406     return hbmNew;
407 }
408 
getSubImage(HBITMAP hbmWhole,const RECT & rcPartial)409 HBITMAP getSubImage(HBITMAP hbmWhole, const RECT& rcPartial)
410 {
411     CRect rc = rcPartial;
412     HBITMAP hbmPart = CreateDIBWithProperties(rc.Width(), rc.Height());
413     if (!hbmPart)
414         return NULL;
415 
416     HDC hDC1 = ::CreateCompatibleDC(NULL);
417     HDC hDC2 = ::CreateCompatibleDC(NULL);
418     HGDIOBJ hbm1Old = ::SelectObject(hDC1, hbmWhole);
419     HGDIOBJ hbm2Old = ::SelectObject(hDC2, hbmPart);
420     ::BitBlt(hDC2, 0, 0, rc.Width(), rc.Height(), hDC1, rc.left, rc.top, SRCCOPY);
421     ::SelectObject(hDC1, hbm1Old);
422     ::SelectObject(hDC2, hbm2Old);
423     ::DeleteDC(hDC1);
424     ::DeleteDC(hDC2);
425     return hbmPart;
426 }
427 
putSubImage(HBITMAP hbmWhole,const RECT & rcPartial,HBITMAP hbmPart)428 void putSubImage(HBITMAP hbmWhole, const RECT& rcPartial, HBITMAP hbmPart)
429 {
430     CRect rc = rcPartial;
431     HDC hDC1 = ::CreateCompatibleDC(NULL);
432     HDC hDC2 = ::CreateCompatibleDC(NULL);
433     HGDIOBJ hbm1Old = ::SelectObject(hDC1, hbmWhole);
434     HGDIOBJ hbm2Old = ::SelectObject(hDC2, hbmPart);
435     ::BitBlt(hDC1, rc.left, rc.top, rc.Width(), rc.Height(), hDC2, 0, 0, SRCCOPY);
436     ::SelectObject(hDC1, hbm1Old);
437     ::SelectObject(hDC2, hbm2Old);
438     ::DeleteDC(hDC1);
439     ::DeleteDC(hDC2);
440 }
441 
442 struct BITMAPINFODX : BITMAPINFO
443 {
444     RGBQUAD bmiColorsAdditional[256 - 1];
445 };
446 
BitmapToClipboardDIB(HBITMAP hBitmap)447 HGLOBAL BitmapToClipboardDIB(HBITMAP hBitmap)
448 {
449     CWaitCursor waitCursor;
450 
451     BITMAP bm;
452     if (!GetObjectW(hBitmap, sizeof(BITMAP), &bm))
453         return NULL;
454 
455     BITMAPINFODX bmi;
456     ZeroMemory(&bmi, sizeof(bmi));
457     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
458     bmi.bmiHeader.biWidth = bm.bmWidth;
459     bmi.bmiHeader.biHeight = bm.bmHeight;
460     bmi.bmiHeader.biPlanes = 1;
461     bmi.bmiHeader.biBitCount = bm.bmBitsPixel;
462     bmi.bmiHeader.biCompression = BI_RGB;
463     bmi.bmiHeader.biSizeImage = bm.bmWidthBytes * bm.bmHeight;
464 
465     INT cColors;
466     if (bm.bmBitsPixel < 16)
467         cColors = 1 << bm.bmBitsPixel;
468     else
469         cColors = 0;
470 
471     HDC hDC = CreateCompatibleDC(NULL);
472 
473     if (cColors)
474     {
475         HGDIOBJ hbmOld = SelectObject(hDC, hBitmap);
476         cColors = GetDIBColorTable(hDC, 0, cColors, bmi.bmiColors);
477         SelectObject(hDC, hbmOld);
478     }
479 
480     DWORD cbColors = cColors * sizeof(RGBQUAD);
481     DWORD dwSize = sizeof(BITMAPINFOHEADER) + cbColors + bmi.bmiHeader.biSizeImage;
482     HGLOBAL hGlobal = GlobalAlloc(GHND | GMEM_SHARE, dwSize);
483     if (hGlobal)
484     {
485         LPBYTE pb = (LPBYTE)GlobalLock(hGlobal);
486         if (pb)
487         {
488             CopyMemory(pb, &bmi, sizeof(BITMAPINFOHEADER));
489             pb += sizeof(BITMAPINFOHEADER);
490 
491             CopyMemory(pb, bmi.bmiColors, cbColors);
492             pb += cbColors;
493 
494             GetDIBits(hDC, hBitmap, 0, bm.bmHeight, pb, &bmi, DIB_RGB_COLORS);
495 
496             GlobalUnlock(hGlobal);
497         }
498         else
499         {
500             GlobalFree(hGlobal);
501             hGlobal = NULL;
502         }
503     }
504 
505     DeleteDC(hDC);
506 
507     return hGlobal;
508 }
509 
BitmapFromClipboardDIB(HGLOBAL hGlobal)510 HBITMAP BitmapFromClipboardDIB(HGLOBAL hGlobal)
511 {
512     CWaitCursor waitCursor;
513 
514     LPBYTE pb = (LPBYTE)GlobalLock(hGlobal);
515     if (!pb)
516         return NULL;
517 
518     LPBITMAPINFO pbmi = (LPBITMAPINFO)pb;
519     pb += pbmi->bmiHeader.biSize;
520 
521     INT cColors = 0, cbColors = 0;
522     if (pbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
523     {
524         LPBITMAPCOREINFO pbmci = (LPBITMAPCOREINFO)pbmi;
525         WORD BitCount = pbmci->bmciHeader.bcBitCount;
526         if (BitCount < 16)
527         {
528             cColors = (1 << BitCount);
529             cbColors = cColors * sizeof(RGBTRIPLE);
530             pb += cbColors;
531         }
532     }
533     else if (pbmi->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER))
534     {
535         WORD BitCount = pbmi->bmiHeader.biBitCount;
536         if (BitCount < 16)
537         {
538             cColors = (1 << BitCount);
539             cbColors = cColors * sizeof(RGBQUAD);
540             pb += cbColors;
541         }
542     }
543 
544     HDC hDC = CreateCompatibleDC(NULL);
545     HBITMAP hBitmap = CreateDIBSection(hDC, pbmi, DIB_RGB_COLORS, NULL, NULL, 0);
546     if (hBitmap)
547     {
548         SetDIBits(hDC, hBitmap, 0, labs(pbmi->bmiHeader.biHeight), pb, pbmi, DIB_RGB_COLORS);
549     }
550     DeleteDC(hDC);
551 
552     GlobalUnlock(hGlobal);
553 
554     return hBitmap;
555 }
556 
BitmapFromHEMF(HENHMETAFILE hEMF)557 HBITMAP BitmapFromHEMF(HENHMETAFILE hEMF)
558 {
559     CWaitCursor waitCursor;
560 
561     ENHMETAHEADER header;
562     if (!GetEnhMetaFileHeader(hEMF, sizeof(header), &header))
563         return NULL;
564 
565     CRect rc = *(LPRECT)&header.rclBounds;
566     INT cx = rc.Width(), cy = rc.Height();
567     HBITMAP hbm = CreateColorDIB(cx, cy, RGB(255, 255, 255));
568     if (!hbm)
569         return NULL;
570 
571     HDC hDC = CreateCompatibleDC(NULL);
572     HGDIOBJ hbmOld = SelectObject(hDC, hbm);
573     PlayEnhMetaFile(hDC, hEMF, &rc);
574     SelectObject(hDC, hbmOld);
575     DeleteDC(hDC);
576 
577     return hbm;
578 }
579 
IsBitmapBlackAndWhite(HBITMAP hbm)580 BOOL IsBitmapBlackAndWhite(HBITMAP hbm)
581 {
582     CWaitCursor waitCursor;
583 
584     BITMAP bm;
585     if (!::GetObjectW(hbm, sizeof(bm), &bm))
586         return FALSE;
587 
588     if (bm.bmBitsPixel == 1)
589         return TRUE;
590 
591     BITMAPINFOEX bmi;
592     ZeroMemory(&bmi, sizeof(bmi));
593     bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
594     bmi.bmiHeader.biWidth = bm.bmWidth;
595     bmi.bmiHeader.biHeight = bm.bmHeight;
596     bmi.bmiHeader.biPlanes = 1;
597     bmi.bmiHeader.biBitCount = 24;
598 
599     DWORD widthbytes = WIDTHBYTES(24 * bm.bmWidth);
600     DWORD cbBits = widthbytes * bm.bmHeight;
601     LPBYTE pbBits = new BYTE[cbBits];
602 
603     HDC hdc = ::CreateCompatibleDC(NULL);
604     ::GetDIBits(hdc, hbm, 0, bm.bmHeight, pbBits, &bmi, DIB_RGB_COLORS);
605     ::DeleteDC(hdc);
606 
607     BOOL bBlackAndWhite = TRUE;
608     for (LONG y = 0; y < bm.bmHeight; ++y)
609     {
610         LPBYTE pbLine = &pbBits[widthbytes * y];
611         for (LONG x = 0; x < bm.bmWidth; ++x)
612         {
613             BYTE Blue = *pbLine++;
614             BYTE Green = *pbLine++;
615             BYTE Red = *pbLine++;
616             COLORREF rgbColor = RGB(Red, Green, Blue);
617             if (rgbColor != RGB(0, 0, 0) && rgbColor != RGB(255, 255, 255))
618             {
619                 bBlackAndWhite = FALSE;
620                 goto Finish;
621             }
622         }
623     }
624 
625 Finish:
626     delete[] pbBits;
627 
628     return bBlackAndWhite;
629 }
630 
ConvertToBlackAndWhite(HBITMAP hbm)631 HBITMAP ConvertToBlackAndWhite(HBITMAP hbm)
632 {
633     CWaitCursor waitCursor;
634 
635     BITMAP bm;
636     if (!::GetObjectW(hbm, sizeof(bm), &bm))
637         return NULL;
638 
639     BITMAPINFOEX bmi;
640     ZeroMemory(&bmi, sizeof(bmi));
641     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
642     bmi.bmiHeader.biWidth = bm.bmWidth;
643     bmi.bmiHeader.biHeight = bm.bmHeight;
644     bmi.bmiHeader.biPlanes = 1;
645     bmi.bmiHeader.biBitCount = 1;
646     bmi.bmiColors[1].rgbBlue = 255;
647     bmi.bmiColors[1].rgbGreen = 255;
648     bmi.bmiColors[1].rgbRed = 255;
649     HDC hdc = ::CreateCompatibleDC(NULL);
650     LPVOID pvMonoBits;
651     HBITMAP hMonoBitmap = ::CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvMonoBits, NULL, 0);
652     if (!hMonoBitmap)
653     {
654         ::DeleteDC(hdc);
655         return NULL;
656     }
657 
658     HBITMAP hNewBitmap = CreateDIBWithProperties(bm.bmWidth, bm.bmHeight);
659     if (hNewBitmap)
660     {
661         ::GetDIBits(hdc, hbm, 0, bm.bmHeight, pvMonoBits, &bmi, DIB_RGB_COLORS);
662         ::SetDIBits(hdc, hNewBitmap, 0, bm.bmHeight, pvMonoBits, &bmi, DIB_RGB_COLORS);
663     }
664     ::DeleteObject(hMonoBitmap);
665     ::DeleteDC(hdc);
666 
667     return hNewBitmap;
668 }
669