xref: /reactos/sdk/lib/atl/atlimage.h (revision 8d74bc4b)
1 // PROJECT:        ReactOS ATL CImage
2 // LICENSE:        Public Domain
3 // PURPOSE:        Provides compatibility to Microsoft ATL
4 // PROGRAMMERS:    Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
5 
6 #ifndef __ATLIMAGE_H__
7 #define __ATLIMAGE_H__
8 
9 #pragma once
10 
11 #include <atlcore.h>        // for ATL Core
12 #include <atlstr.h>         // for CAtlStringMgr
13 #include <atlsimpstr.h>     // for CSimpleString
14 #include <atlsimpcoll.h>    // for CSimpleArray
15 
16 #include <wingdi.h>
17 #include <cguid.h>          // for GUID_NULL
18 #include <gdiplus.h>        // GDI+
19 
20 namespace ATL
21 {
22 
23 class CImage
24 {
25 public:
26     // flags for CImage::Create/CreateEx
27     enum
28     {
29         createAlphaChannel = 1      // enable alpha
30     };
31 
32     // orientation of DIB
33     enum DIBOrientation
34     {
35         DIBOR_DEFAULT,              // default
36         DIBOR_TOPDOWN,              // top-down DIB
37         DIBOR_BOTTOMUP              // bottom-up DIB
38     };
39 
CImage()40     CImage() noexcept
41     {
42         s_gdiplus.IncreaseCImageCount();
43     }
44 
~CImage()45     virtual ~CImage() noexcept
46     {
47         Destroy();
48         s_gdiplus.DecreaseCImageCount();
49     }
50 
HBITMAP()51     operator HBITMAP() noexcept
52     {
53         return m_hBitmap;
54     }
55 
ReleaseGDIPlus()56     static void ReleaseGDIPlus()
57     {
58         s_gdiplus.ReleaseGDIPlus();
59     }
60 
61     void Attach(HBITMAP hBitmap, DIBOrientation eOrientation = DIBOR_DEFAULT) noexcept
62     {
63         AttachInternal(hBitmap, eOrientation, -1);
64     }
65 
Detach()66     HBITMAP Detach() noexcept
67     {
68         m_hOldBitmap = NULL;
69         m_nWidth = m_nHeight = m_nPitch = m_nBPP = 0;
70         m_pBits = NULL;
71 
72         m_bHasAlphaChannel = m_bIsDIBSection = FALSE;
73         m_clrTransparentColor = CLR_INVALID;
74 
75         HBITMAP hBitmap = m_hBitmap;
76         m_hBitmap = NULL;
77         return hBitmap;
78     }
79 
GetDC()80     HDC GetDC() const noexcept
81     {
82         ATLASSERT(m_nDCRefCount >= 0);
83         if (::InterlockedIncrement(&m_nDCRefCount) == 1)
84         {
85             ATLASSERT(m_hDC == NULL);
86             ATLASSERT(m_hOldBitmap == NULL);
87 
88             m_hDC = ::CreateCompatibleDC(NULL);
89             ATLASSERT(m_hDC != NULL);
90 
91             m_hOldBitmap = (HBITMAP)::SelectObject(m_hDC, m_hBitmap);
92             ATLASSERT(m_hOldBitmap != NULL);
93         }
94 
95         return m_hDC;
96     }
97 
ReleaseDC()98     void ReleaseDC() const noexcept
99     {
100         ATLASSERT(m_nDCRefCount > 0);
101 
102         if (::InterlockedDecrement(&m_nDCRefCount) != 0)
103             return;
104 
105         if (m_hOldBitmap)
106         {
107             ATLASSERT(m_hDC != NULL);
108             ::SelectObject(m_hDC, m_hOldBitmap);
109             m_hOldBitmap = NULL;
110         }
111 
112         if (m_hDC)
113         {
114             ::DeleteDC(m_hDC);
115             m_hDC = NULL;
116         }
117     }
118 
119     BOOL AlphaBlend(HDC hDestDC,
120         int xDest, int yDest, int nDestWidth, int nDestHeight,
121         int xSrc, int ySrc, int nSrcWidth, int nSrcHeight,
122         BYTE bSrcAlpha = 0xFF, BYTE bBlendOp = AC_SRC_OVER) const
123     {
124         ATLASSERT(IsTransparencySupported());
125 
126         BLENDFUNCTION bf;
127         bf.BlendOp = bBlendOp;
128         bf.BlendFlags = 0;
129         bf.SourceConstantAlpha = bSrcAlpha;
130         bf.AlphaFormat = AC_SRC_ALPHA;
131 
132         GetDC();
133         BOOL ret = ::AlphaBlend(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
134                                 m_hDC, xSrc, ySrc, nSrcWidth, nSrcHeight, bf);
135         ReleaseDC();
136         return ret;
137     }
138     BOOL AlphaBlend(HDC hDestDC, int xDest, int yDest,
139                     BYTE bSrcAlpha = 0xFF, BYTE bBlendOp = AC_SRC_OVER) const
140     {
141         int width = GetWidth();
142         int height = GetHeight();
143         return AlphaBlend(hDestDC, xDest, yDest, width, height, 0, 0,
144                           width, height, bSrcAlpha, bBlendOp);
145     }
146     BOOL AlphaBlend(HDC hDestDC, const POINT& pointDest,
147                     BYTE bSrcAlpha = 0xFF, BYTE bBlendOp = AC_SRC_OVER) const
148     {
149         return AlphaBlend(hDestDC, pointDest.x, pointDest.y, bSrcAlpha, bBlendOp);
150     }
151     BOOL AlphaBlend(HDC hDestDC, const RECT& rectDest, const RECT& rectSrc,
152                     BYTE bSrcAlpha = 0xFF, BYTE bBlendOp = AC_SRC_OVER) const
153     {
154         return AlphaBlend(hDestDC, rectDest.left, rectDest.top,
155                           rectDest.right - rectDest.left,
156                           rectDest.bottom - rectDest.top,
157                           rectSrc.left, rectSrc.top,
158                           rectSrc.right - rectSrc.left,
159                           rectSrc.bottom - rectSrc.top,
160                           bSrcAlpha, bBlendOp);
161     }
162 
163     BOOL BitBlt(HDC hDestDC, int xDest, int yDest,
164                 int nDestWidth, int nDestHeight,
165                 int xSrc, int ySrc, DWORD dwROP = SRCCOPY) const noexcept
166     {
167         GetDC();
168         BOOL ret = ::BitBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
169                             m_hDC, xSrc, ySrc, dwROP);
170         ReleaseDC();
171         return ret;
172     }
173     BOOL BitBlt(HDC hDestDC, int xDest, int yDest,
174                 DWORD dwROP = SRCCOPY) const noexcept
175     {
176         return BitBlt(hDestDC, xDest, yDest,
177                       GetWidth(), GetHeight(), 0, 0, dwROP);
178     }
179     BOOL BitBlt(HDC hDestDC, const POINT& pointDest,
180                 DWORD dwROP = SRCCOPY) const noexcept
181     {
182         return BitBlt(hDestDC, pointDest.x, pointDest.y, dwROP);
183     }
184     BOOL BitBlt(HDC hDestDC, const RECT& rectDest, const POINT& pointSrc,
185                 DWORD dwROP = SRCCOPY) const noexcept
186     {
187         return BitBlt(hDestDC, rectDest.left, rectDest.top,
188                       rectDest.right - rectDest.left,
189                       rectDest.bottom - rectDest.top,
190                       pointSrc.x, pointSrc.y, dwROP);
191     }
192 
193     BOOL Create(int nWidth, int nHeight, int nBPP, DWORD dwFlags = 0) noexcept
194     {
195         return CreateEx(nWidth, nHeight, nBPP, BI_RGB, NULL, dwFlags);
196     }
197 
198     BOOL CreateEx(int nWidth, int nHeight, int nBPP, DWORD eCompression,
199                   const DWORD* pdwBitmasks = NULL, DWORD dwFlags = 0) noexcept
200     {
201         return CreateInternal(nWidth, nHeight, nBPP, eCompression, pdwBitmasks, dwFlags);
202     }
203 
Destroy()204     void Destroy() noexcept
205     {
206         if (m_hBitmap)
207         {
208             ::DeleteObject(Detach());
209         }
210     }
211 
Draw(HDC hDestDC,int xDest,int yDest,int nDestWidth,int nDestHeight,int xSrc,int ySrc,int nSrcWidth,int nSrcHeight)212     BOOL Draw(HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight,
213               int xSrc, int ySrc, int nSrcWidth, int nSrcHeight) const noexcept
214     {
215         ATLASSERT(IsTransparencySupported());
216         if (m_bHasAlphaChannel)
217         {
218             return AlphaBlend(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
219                               xSrc, ySrc, nSrcWidth, nSrcHeight);
220         }
221         else if (m_clrTransparentColor != CLR_INVALID)
222         {
223             COLORREF rgb;
224             if ((m_clrTransparentColor & 0xFF000000) == 0x01000000)
225                 rgb = RGBFromPaletteIndex(m_clrTransparentColor & 0xFF);
226             else
227                 rgb = m_clrTransparentColor;
228             return TransparentBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
229                                   xSrc, ySrc, nSrcWidth, nSrcHeight, rgb);
230         }
231         else
232         {
233             return StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
234                               xSrc, ySrc, nSrcWidth, nSrcHeight);
235         }
236     }
Draw(HDC hDestDC,const RECT & rectDest,const RECT & rectSrc)237     BOOL Draw(HDC hDestDC, const RECT& rectDest, const RECT& rectSrc) const noexcept
238     {
239         return Draw(hDestDC, rectDest.left, rectDest.top,
240                     rectDest.right - rectDest.left,
241                     rectDest.bottom - rectDest.top,
242                     rectSrc.left, rectSrc.top,
243                     rectSrc.right - rectSrc.left,
244                     rectSrc.bottom - rectSrc.top);
245     }
Draw(HDC hDestDC,int xDest,int yDest)246     BOOL Draw(HDC hDestDC, int xDest, int yDest) const noexcept
247     {
248         return Draw(hDestDC, xDest, yDest, GetWidth(), GetHeight());
249     }
Draw(HDC hDestDC,const POINT & pointDest)250     BOOL Draw(HDC hDestDC, const POINT& pointDest) const noexcept
251     {
252         return Draw(hDestDC, pointDest.x, pointDest.y);
253     }
Draw(HDC hDestDC,int xDest,int yDest,int nDestWidth,int nDestHeight)254     BOOL Draw(HDC hDestDC, int xDest, int yDest,
255               int nDestWidth, int nDestHeight) const noexcept
256     {
257         return Draw(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
258                     0, 0, GetWidth(), GetHeight());
259     }
Draw(HDC hDestDC,const RECT & rectDest)260     BOOL Draw(HDC hDestDC, const RECT& rectDest) const noexcept
261     {
262         return Draw(hDestDC, rectDest.left, rectDest.top,
263                     rectDest.right - rectDest.left,
264                     rectDest.bottom - rectDest.top);
265     }
266 
GetBits()267     void *GetBits() noexcept
268     {
269         ATLASSERT(IsDIBSection());
270         return m_pBits;
271     }
272 
GetBits()273     const void *GetBits() const noexcept
274     {
275         return const_cast<CImage*>(this)->GetBits();
276     }
277 
GetBPP()278     int GetBPP() const noexcept
279     {
280         ATLASSERT(m_hBitmap);
281         return m_nBPP;
282     }
283 
GetColorTable(UINT iFirstColor,UINT nColors,RGBQUAD * prgbColors)284     void GetColorTable(UINT iFirstColor, UINT nColors,
285                        RGBQUAD* prgbColors) const noexcept
286     {
287         ATLASSERT(IsDIBSection());
288         GetDC();
289         ::GetDIBColorTable(m_hDC, iFirstColor, nColors, prgbColors);
290         ReleaseDC();
291     }
292 
GetHeight()293     int GetHeight() const noexcept
294     {
295         ATLASSERT(m_hBitmap);
296         return m_nHeight;
297     }
298 
GetMaxColorTableEntries()299     int GetMaxColorTableEntries() const noexcept
300     {
301         ATLASSERT(IsDIBSection());
302         switch (m_nBPP)
303         {
304             case 1: case 4: case 8:
305                 return (1 << m_nBPP);
306 
307             case 16: case 24: case 32:
308             default:
309                 return 0;
310         }
311     }
312 
GetPitch()313     int GetPitch() const noexcept
314     {
315         ATLASSERT(IsDIBSection());
316         return m_nPitch;
317     }
318 
GetPixel(int x,int y)319     COLORREF GetPixel(int x, int y) const noexcept
320     {
321         GetDC();
322         COLORREF ret = ::GetPixel(m_hDC, x, y);
323         ReleaseDC();
324         return ret;
325     }
326 
GetPixelAddress(int x,int y)327     void* GetPixelAddress(int x, int y) noexcept
328     {
329         ATLASSERT(IsDIBSection());
330         BYTE *pb = (BYTE *)GetBits();
331         pb += GetPitch() * y;
332         pb += (GetBPP() * x) / 8;
333         return pb;
334     }
335 
GetPixelAddress(int x,int y)336     const void* GetPixelAddress(int x, int y) const noexcept
337     {
338         return const_cast<CImage*>(this)->GetPixelAddress(x, y);
339     }
340 
GetTransparentColor()341     COLORREF GetTransparentColor() const noexcept
342     {
343         return m_clrTransparentColor;
344     }
345 
GetWidth()346     int GetWidth() const noexcept
347     {
348         ATLASSERT(m_hBitmap);
349         return m_nWidth;
350     }
351 
IsDIBSection()352     bool IsDIBSection() const noexcept
353     {
354         ATLASSERT(m_hBitmap);
355         return m_bIsDIBSection;
356     }
357 
IsIndexed()358     bool IsIndexed() const noexcept
359     {
360         ATLASSERT(IsDIBSection());
361         return GetBPP() <= 8;
362     }
363 
IsNull()364     bool IsNull() const noexcept
365     {
366         return m_hBitmap == NULL;
367     }
368 
Load(LPCTSTR pszFileName)369     HRESULT Load(LPCTSTR pszFileName) noexcept
370     {
371         if (!InitGDIPlus())
372             return E_FAIL;
373 
374         // convert the file name string into Unicode
375         CStringW pszNameW(pszFileName);
376 
377         // create a GpBitmap object from file
378         using namespace Gdiplus;
379         GpBitmap *pBitmap = NULL;
380         if (s_gdiplus.CreateBitmapFromFile(pszNameW, &pBitmap) != Ok)
381         {
382             return E_FAIL;
383         }
384 
385         // get bitmap handle
386         HBITMAP hbm = NULL;
387         Color color(0xFF, 0xFF, 0xFF);
388         Status status = s_gdiplus.CreateHBITMAPFromBitmap(pBitmap, &hbm, color.GetValue());
389 
390         // delete GpBitmap
391         s_gdiplus.DisposeImage(pBitmap);
392 
393         // attach it
394         if (status == Ok)
395             Attach(hbm);
396         return (status == Ok ? S_OK : E_FAIL);
397     }
Load(IStream * pStream)398     HRESULT Load(IStream* pStream) noexcept
399     {
400         if (!InitGDIPlus())
401             return E_FAIL;
402 
403         // create GpBitmap from stream
404         using namespace Gdiplus;
405         GpBitmap *pBitmap = NULL;
406         if (s_gdiplus.CreateBitmapFromStream(pStream, &pBitmap) != Ok)
407         {
408             return E_FAIL;
409         }
410 
411         // get bitmap handle
412         HBITMAP hbm = NULL;
413         Color color(0xFF, 0xFF, 0xFF);
414         Status status = s_gdiplus.CreateHBITMAPFromBitmap(pBitmap, &hbm, color.GetValue());
415 
416         // delete Bitmap
417         s_gdiplus.DisposeImage(pBitmap);
418 
419         // attach it
420         if (status == Ok)
421             Attach(hbm);
422         return (status == Ok ? S_OK : E_FAIL);
423     }
424 
425     // NOTE: LoadFromResource loads BITMAP resource only
LoadFromResource(HINSTANCE hInstance,LPCTSTR pszResourceName)426     void LoadFromResource(HINSTANCE hInstance, LPCTSTR pszResourceName) noexcept
427     {
428         HANDLE hHandle = ::LoadImage(hInstance, pszResourceName,
429                                      IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
430         Attach(reinterpret_cast<HBITMAP>(hHandle));
431     }
LoadFromResource(HINSTANCE hInstance,UINT nIDResource)432     void LoadFromResource(HINSTANCE hInstance, UINT nIDResource) noexcept
433     {
434         LoadFromResource(hInstance, MAKEINTRESOURCE(nIDResource));
435     }
436 
437     BOOL MaskBlt(HDC hDestDC, int xDest, int yDest,
438                  int nDestWidth, int nDestHeight, int xSrc, int ySrc,
439                  HBITMAP hbmMask, int xMask, int yMask,
440                  DWORD dwROP = SRCCOPY) const noexcept
441     {
442         ATLASSERT(IsTransparencySupported());
443         GetDC();
444         BOOL ret = ::MaskBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
445                              m_hDC, xSrc, ySrc,
446                              hbmMask, xMask, yMask, dwROP);
447         ReleaseDC();
448         return ret;
449     }
450     BOOL MaskBlt(HDC hDestDC, const RECT& rectDest, const POINT& pointSrc,
451                  HBITMAP hbmMask, const POINT& pointMask,
452                  DWORD dwROP = SRCCOPY) const noexcept
453     {
454         return MaskBlt(hDestDC, rectDest.left, rectDest.top,
455             rectDest.right - rectDest.left, rectDest.bottom - rectDest.top,
456             pointSrc.x, pointSrc.y, hbmMask, pointMask.x, pointMask.y, dwROP);
457     }
458     BOOL MaskBlt(HDC hDestDC, int xDest, int yDest,
459                  HBITMAP hbmMask, DWORD dwROP = SRCCOPY) const noexcept
460     {
461         return MaskBlt(hDestDC, xDest, yDest, GetWidth(), GetHeight(),
462                        0, 0, hbmMask, 0, 0, dwROP);
463     }
464     BOOL MaskBlt(HDC hDestDC, const POINT& pointDest,
465                  HBITMAP hbmMask, DWORD dwROP = SRCCOPY) const noexcept
466     {
467         return MaskBlt(hDestDC, pointDest.x, pointDest.y, hbmMask, dwROP);
468     }
469 
470     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints,
471                 int xSrc, int ySrc, int nSrcWidth, int nSrcHeight,
472                 HBITMAP hbmMask = NULL,
473                 int xMask = 0, int yMask = 0) const noexcept
474     {
475         ATLASSERT(IsTransparencySupported());
476         GetDC();
477         BOOL ret = ::PlgBlt(hDestDC, pPoints, m_hDC,
478                             xSrc, ySrc, nSrcWidth, nSrcHeight,
479                             hbmMask, xMask, yMask);
480         ReleaseDC();
481         return ret;
482     }
483     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints,
484                 HBITMAP hbmMask = NULL) const noexcept
485     {
486         return PlgBlt(hDestDC, pPoints, 0, 0, GetWidth(), GetHeight(),
487                       hbmMask);
488     }
PlgBlt(HDC hDestDC,const POINT * pPoints,const RECT & rectSrc,HBITMAP hbmMask,const POINT & pointMask)489     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints, const RECT& rectSrc,
490                 HBITMAP hbmMask, const POINT& pointMask) const noexcept
491     {
492         return PlgBlt(hDestDC, pPoints, rectSrc.left, rectSrc.top,
493             rectSrc.right - rectSrc.left, rectSrc.bottom - rectSrc.top,
494             hbmMask, pointMask.x, pointMask.y);
495     }
496     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints, const RECT& rectSrc,
497                 HBITMAP hbmMask = NULL) const noexcept
498     {
499         POINT pointMask = {0, 0};
500         return PlgBlt(hDestDC, pPoints, rectSrc, hbmMask, pointMask);
501     }
502 
Save(IStream * pStream,GUID * guidFileType)503     HRESULT Save(IStream* pStream, GUID *guidFileType) const noexcept
504     {
505         if (!InitGDIPlus())
506             return E_FAIL;
507 
508         using namespace Gdiplus;
509         ATLASSERT(m_hBitmap);
510 
511         // Get encoders
512         UINT cEncoders = 0;
513         ImageCodecInfo* pEncoders = _getAllEncoders(cEncoders);
514 
515         // Get Codec
516         CLSID clsid = FindCodecForFileType(*guidFileType, pEncoders, cEncoders);
517         delete[] pEncoders;
518 
519         // create a GpBitmap from HBITMAP
520         GpBitmap *pBitmap = NULL;
521         s_gdiplus.CreateBitmapFromHBITMAP(m_hBitmap, NULL, &pBitmap);
522 
523         // save to stream
524         Status status;
525         status = s_gdiplus.SaveImageToStream(pBitmap, pStream, &clsid, NULL);
526 
527         // destroy GpBitmap
528         s_gdiplus.DisposeImage(pBitmap);
529 
530         return (status == Ok ? S_OK : E_FAIL);
531     }
532 
533     HRESULT Save(LPCTSTR pszFileName,
534                  REFGUID guidFileType = GUID_NULL) const noexcept
535     {
536         if (!InitGDIPlus())
537             return E_FAIL;
538 
539         using namespace Gdiplus;
540         ATLASSERT(m_hBitmap);
541 
542         // convert the file name string into Unicode
543         CStringW pszNameW(pszFileName);
544 
545         // Get encoders
546         UINT cEncoders = 0;
547         ImageCodecInfo* pEncoders = _getAllEncoders(cEncoders);
548 
549         // if the file type is null, get the file type from extension
550         CLSID clsid;
551         if (::IsEqualGUID(guidFileType, GUID_NULL))
552         {
553             CString strExt(GetFileExtension(pszNameW));
554             clsid = FindCodecForExtension(strExt, pEncoders, cEncoders);
555         }
556         else
557         {
558             clsid = FindCodecForFileType(guidFileType, pEncoders, cEncoders);
559         }
560         delete[] pEncoders;
561 
562         // create a GpBitmap from HBITMAP
563         GpBitmap *pBitmap = NULL;
564         s_gdiplus.CreateBitmapFromHBITMAP(m_hBitmap, NULL, &pBitmap);
565 
566         // save to file
567         Status status = s_gdiplus.SaveImageToFile(pBitmap, pszNameW, &clsid, NULL);
568 
569         // destroy GpBitmap
570         s_gdiplus.DisposeImage(pBitmap);
571 
572         return (status == Ok ? S_OK : E_FAIL);
573     }
574 
SetColorTable(UINT iFirstColor,UINT nColors,const RGBQUAD * prgbColors)575     void SetColorTable(UINT iFirstColor, UINT nColors,
576                        const RGBQUAD* prgbColors) noexcept
577     {
578         ATLASSERT(IsDIBSection());
579         GetDC();
580         ::SetDIBColorTable(m_hDC, iFirstColor, nColors, prgbColors);
581         ReleaseDC();
582     }
583 
SetPixel(int x,int y,COLORREF color)584     void SetPixel(int x, int y, COLORREF color) noexcept
585     {
586         GetDC();
587         ::SetPixelV(m_hDC, x, y, color);
588         ReleaseDC();
589     }
590 
SetPixelIndexed(int x,int y,int iIndex)591     void SetPixelIndexed(int x, int y, int iIndex) noexcept
592     {
593         ATLASSERT(IsIndexed());
594         GetDC();
595         ::SetPixelV(m_hDC, x, y, PALETTEINDEX(iIndex));
596         ReleaseDC();
597     }
598 
SetPixelRGB(int x,int y,BYTE r,BYTE g,BYTE b)599     void SetPixelRGB(int x, int y, BYTE r, BYTE g, BYTE b) noexcept
600     {
601         SetPixel(x, y, RGB(r, g, b));
602     }
603 
SetTransparentColor(COLORREF rgbTransparent)604     COLORREF SetTransparentColor(COLORREF rgbTransparent) noexcept
605     {
606         ATLASSERT(m_hBitmap);
607         COLORREF rgbOldColor = m_clrTransparentColor;
608         m_clrTransparentColor = rgbTransparent;
609         return rgbOldColor;
610     }
611 
612     BOOL StretchBlt(HDC hDestDC, int xDest, int yDest,
613                     int nDestWidth, int nDestHeight,
614                     int xSrc, int ySrc, int nSrcWidth, int nSrcHeight,
615                     DWORD dwROP = SRCCOPY) const noexcept
616     {
617         GetDC();
618         BOOL ret = ::StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
619                                 m_hDC, xSrc, ySrc, nSrcWidth, nSrcHeight, dwROP);
620         ReleaseDC();
621         return ret;
622     }
623     BOOL StretchBlt(HDC hDestDC, int xDest, int yDest,
624                     int nDestWidth, int nDestHeight,
625                     DWORD dwROP = SRCCOPY) const noexcept
626     {
627         return StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
628                           0, 0, GetWidth(), GetHeight(), dwROP);
629     }
630     BOOL StretchBlt(HDC hDestDC, const RECT& rectDest,
631                     DWORD dwROP = SRCCOPY) const noexcept
632     {
633         return StretchBlt(hDestDC, rectDest.left, rectDest.top,
634                           rectDest.right - rectDest.left,
635                           rectDest.bottom - rectDest.top, dwROP);
636     }
637     BOOL StretchBlt(HDC hDestDC, const RECT& rectDest,
638                     const RECT& rectSrc, DWORD dwROP = SRCCOPY) const noexcept
639     {
640         return StretchBlt(hDestDC, rectDest.left, rectDest.top,
641                           rectDest.right - rectDest.left,
642                           rectDest.bottom - rectDest.top,
643                           rectSrc.left, rectSrc.top,
644                           rectSrc.right - rectSrc.left,
645                           rectSrc.bottom - rectSrc.top, dwROP);
646     }
647 
648     BOOL TransparentBlt(HDC hDestDC, int xDest, int yDest,
649                         int nDestWidth, int nDestHeight,
650                         int xSrc, int ySrc, int nSrcWidth, int nSrcHeight,
651                         UINT crTransparent = CLR_INVALID) const noexcept
652     {
653         ATLASSERT(IsTransparencySupported());
654         GetDC();
655         BOOL ret = ::TransparentBlt(hDestDC, xDest, yDest,
656                                     nDestWidth, nDestHeight,
657                                     m_hDC, xSrc, ySrc,
658                                     nSrcWidth, nSrcHeight, crTransparent);
659         ReleaseDC();
660         return ret;
661     }
662     BOOL TransparentBlt(HDC hDestDC, int xDest, int yDest,
663                         int nDestWidth, int nDestHeight,
664                         UINT crTransparent = CLR_INVALID) const noexcept
665     {
666         return TransparentBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
667                               0, 0, GetWidth(), GetHeight(), crTransparent);
668     }
669     BOOL TransparentBlt(HDC hDestDC, const RECT& rectDest,
670                         UINT crTransparent = CLR_INVALID) const noexcept
671     {
672         return TransparentBlt(hDestDC, rectDest.left, rectDest.top,
673                               rectDest.right - rectDest.left,
674                               rectDest.bottom - rectDest.top, crTransparent);
675     }
676     BOOL TransparentBlt(
677        HDC hDestDC, const RECT& rectDest,
678        const RECT& rectSrc, UINT crTransparent = CLR_INVALID) const noexcept
679     {
680         return TransparentBlt(hDestDC, rectDest.left, rectDest.top,
681             rectDest.right - rectDest.left, rectDest.bottom - rectDest.left,
682             rectSrc.left, rectSrc.top, rectSrc.right - rectSrc.left,
683             rectSrc.bottom - rectSrc.top, crTransparent);
684     }
685 
IsTransparencySupported()686     static BOOL IsTransparencySupported() noexcept
687     {
688         return TRUE;
689     }
690 
691     enum ExcludeFlags
692     {
693         excludeGIF          = 0x01,
694         excludeBMP          = 0x02,
695         excludeEMF          = 0x04,
696         excludeWMF          = 0x08,
697         excludeJPEG         = 0x10,
698         excludePNG          = 0x20,
699         excludeTIFF         = 0x40,
700         excludeIcon         = 0x80,
701         excludeOther        = 0x80000000,
702         excludeDefaultLoad  = 0,
703         excludeDefaultSave  = excludeIcon | excludeEMF | excludeWMF
704     };
705 
706 private:
ShouldExcludeFormat(REFGUID guidFileType,DWORD dwExclude)707     static bool ShouldExcludeFormat(REFGUID guidFileType, DWORD dwExclude)
708     {
709         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatGIF))
710             return !!(dwExclude & excludeGIF);
711         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatBMP))
712             return !!(dwExclude & excludeBMP);
713         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatEMF))
714             return !!(dwExclude & excludeEMF);
715         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatWMF))
716             return !!(dwExclude & excludeWMF);
717         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatJPEG))
718             return !!(dwExclude & excludeJPEG);
719         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatPNG))
720             return !!(dwExclude & excludePNG);
721         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatTIFF))
722             return !!(dwExclude & excludeTIFF);
723         if (::IsEqualGUID(guidFileType, Gdiplus::ImageFormatIcon))
724             return !!(dwExclude & excludeIcon);
725         return ((dwExclude & excludeOther) == excludeOther);
726     }
727 
BuildCodecFilterString(const Gdiplus::ImageCodecInfo * pCodecs,UINT cCodecs,CSimpleString & strFilter,CSimpleArray<GUID> & aguidFileTypes,LPCTSTR pszAllFilesDescription,DWORD dwExclude,TCHAR chSeparator)728     static HRESULT BuildCodecFilterString(
729         const Gdiplus::ImageCodecInfo* pCodecs,
730         UINT cCodecs,
731         CSimpleString& strFilter,
732         CSimpleArray<GUID>& aguidFileTypes,
733         LPCTSTR pszAllFilesDescription,
734         DWORD dwExclude,
735         TCHAR chSeparator)
736     {
737         if (!pCodecs || !cCodecs)
738         {
739             strFilter += chSeparator;
740             return E_FAIL;
741         }
742 
743         if (pszAllFilesDescription)
744         {
745             strFilter += pszAllFilesDescription;
746 
747             BOOL bFirst = TRUE;
748             CString extensions;
749             for (UINT i = 0; i < cCodecs; ++i)
750             {
751                 if (ShouldExcludeFormat(pCodecs[i].FormatID, dwExclude))
752                     continue;
753 
754                 if (bFirst)
755                     bFirst = FALSE;
756                 else
757                     extensions += TEXT(';');
758 
759                 CString ext(pCodecs[i].FilenameExtension);
760                 extensions += ext;
761             }
762 
763             strFilter += chSeparator;
764             strFilter += extensions;
765             strFilter += chSeparator;
766 
767             aguidFileTypes.Add(GUID_NULL);
768         }
769 
770         for (UINT i = 0; i < cCodecs; ++i)
771         {
772             if (ShouldExcludeFormat(pCodecs[i].FormatID, dwExclude))
773                 continue;
774 
775             CString extensions(pCodecs[i].FilenameExtension);
776 
777             CString desc(pCodecs[i].FormatDescription);
778             strFilter += desc;
779             strFilter += TEXT(" (");
780             strFilter += extensions;
781             strFilter += TEXT(")");
782             strFilter += chSeparator;
783             strFilter += extensions;
784             strFilter += chSeparator;
785 
786             aguidFileTypes.Add(pCodecs[i].FormatID);
787         }
788 
789         strFilter += chSeparator;
790         return S_OK;
791     }
792 
793 public:
794     static HRESULT GetImporterFilterString(
795         CSimpleString& strImporters,
796         CSimpleArray<GUID>& aguidFileTypes,
797         LPCTSTR pszAllFilesDescription = NULL,
798         DWORD dwExclude = excludeDefaultLoad,
799         TCHAR chSeparator = TEXT('|'))
800     {
801         if (!InitGDIPlus())
802             return E_FAIL;
803 
804         UINT cDecoders = 0;
805         Gdiplus::ImageCodecInfo* pDecoders = _getAllDecoders(cDecoders);
806         HRESULT hr = BuildCodecFilterString(pDecoders,
807                                             cDecoders,
808                                             strImporters,
809                                             aguidFileTypes,
810                                             pszAllFilesDescription,
811                                             dwExclude,
812                                             chSeparator);
813         delete[] pDecoders;
814         return hr;
815     }
816 
817     static HRESULT GetExporterFilterString(
818         CSimpleString& strExporters,
819         CSimpleArray<GUID>& aguidFileTypes,
820         LPCTSTR pszAllFilesDescription = NULL,
821         DWORD dwExclude = excludeDefaultSave,
822         TCHAR chSeparator = TEXT('|'))
823     {
824         if (!InitGDIPlus())
825             return E_FAIL;
826 
827         UINT cEncoders = 0;
828         Gdiplus::ImageCodecInfo* pEncoders = _getAllEncoders(cEncoders);
829         HRESULT hr = BuildCodecFilterString(pEncoders,
830                                             cEncoders,
831                                             strExporters,
832                                             aguidFileTypes,
833                                             pszAllFilesDescription,
834                                             dwExclude,
835                                             chSeparator);
836         delete[] pEncoders;
837         return hr;
838     }
839 
840 private:
841     // an extension of BITMAPINFO
842     struct MYBITMAPINFOEX : BITMAPINFO
843     {
844         RGBQUAD bmiColorsExtra[256 - 1];
845     };
846 
847     class CInitGDIPlus
848     {
849     private:
850         // GDI+ function types
851         using FUN_Startup = decltype(&Gdiplus::GdiplusStartup);
852         using FUN_Shutdown = decltype(&Gdiplus::GdiplusShutdown);
853         using FUN_CreateBitmapFromFile = decltype(&Gdiplus::DllExports::GdipCreateBitmapFromFile);
854         using FUN_CreateBitmapFromHBITMAP = decltype(&Gdiplus::DllExports::GdipCreateBitmapFromHBITMAP);
855         using FUN_CreateBitmapFromStream = decltype(&Gdiplus::DllExports::GdipCreateBitmapFromStream);
856         using FUN_CreateHBITMAPFromBitmap = decltype(&Gdiplus::DllExports::GdipCreateHBITMAPFromBitmap);
857         using FUN_DisposeImage = decltype(&Gdiplus::DllExports::GdipDisposeImage);
858         using FUN_GetImageDecoder = decltype(&Gdiplus::DllExports::GdipGetImageDecoders);
859         using FUN_GetImageDecoderSize = decltype(&Gdiplus::DllExports::GdipGetImageDecodersSize);
860         using FUN_GetImageEncoder = decltype(&Gdiplus::DllExports::GdipGetImageEncoders);
861         using FUN_GetImageEncoderSize = decltype(&Gdiplus::DllExports::GdipGetImageEncodersSize);
862         using FUN_SaveImageToFile = decltype(&Gdiplus::DllExports::GdipSaveImageToFile);
863         using FUN_SaveImageToStream = decltype(&Gdiplus::DllExports::GdipSaveImageToStream);
864 
865         // members
866         HINSTANCE m_hInst;
867         ULONG_PTR m_dwToken;
868         CRITICAL_SECTION m_sect;
869         LONG m_nCImageObjects;
870         DWORD m_dwLastError;
871 
_clear_funs()872         void _clear_funs() noexcept
873         {
874             Startup = NULL;
875             Shutdown = NULL;
876             CreateBitmapFromFile = NULL;
877             CreateBitmapFromHBITMAP = NULL;
878             CreateBitmapFromStream = NULL;
879             CreateHBITMAPFromBitmap = NULL;
880             DisposeImage = NULL;
881             GetImageDecoders = NULL;
882             GetImageDecodersSize = NULL;
883             GetImageEncoders = NULL;
884             GetImageEncodersSize = NULL;
885             SaveImageToFile = NULL;
886             SaveImageToStream = NULL;
887         }
888 
889         template <typename T_FUN>
_get_fun(T_FUN & fun,LPCSTR name)890         T_FUN _get_fun(T_FUN& fun, LPCSTR name) noexcept
891         {
892             if (!fun)
893                 fun = reinterpret_cast<T_FUN>(::GetProcAddress(m_hInst, name));
894             return fun;
895         }
896 
897     public:
898         // GDI+ functions
899         FUN_Startup                 Startup;
900         FUN_Shutdown                Shutdown;
901         FUN_CreateBitmapFromFile    CreateBitmapFromFile;
902         FUN_CreateBitmapFromHBITMAP CreateBitmapFromHBITMAP;
903         FUN_CreateBitmapFromStream  CreateBitmapFromStream;
904         FUN_CreateHBITMAPFromBitmap CreateHBITMAPFromBitmap;
905         FUN_DisposeImage            DisposeImage;
906         FUN_GetImageDecoder         GetImageDecoders;
907         FUN_GetImageDecoderSize     GetImageDecodersSize;
908         FUN_GetImageEncoder         GetImageEncoders;
909         FUN_GetImageEncoderSize     GetImageEncodersSize;
910         FUN_SaveImageToFile         SaveImageToFile;
911         FUN_SaveImageToStream       SaveImageToStream;
912 
CInitGDIPlus()913         CInitGDIPlus() noexcept
914             : m_hInst(NULL)
915             , m_dwToken(0)
916             , m_nCImageObjects(0)
917             , m_dwLastError(ERROR_SUCCESS)
918         {
919             _clear_funs();
920             ::InitializeCriticalSection(&m_sect);
921         }
922 
~CInitGDIPlus()923         ~CInitGDIPlus() noexcept
924         {
925             ReleaseGDIPlus();
926             ::DeleteCriticalSection(&m_sect);
927         }
928 
Init()929         bool Init() noexcept
930         {
931             ::EnterCriticalSection(&m_sect);
932 
933             if (m_dwToken == 0)
934             {
935                 if (!m_hInst)
936                     m_hInst = ::LoadLibrary(TEXT("gdiplus.dll"));
937 
938                 if (m_hInst &&
939                     _get_fun(Startup, "GdiplusStartup") &&
940                     _get_fun(Shutdown, "GdiplusShutdown") &&
941                     _get_fun(CreateBitmapFromFile, "GdipCreateBitmapFromFile") &&
942                     _get_fun(CreateBitmapFromHBITMAP, "GdipCreateBitmapFromHBITMAP") &&
943                     _get_fun(CreateBitmapFromStream, "GdipCreateBitmapFromStream") &&
944                     _get_fun(CreateHBITMAPFromBitmap, "GdipCreateHBITMAPFromBitmap") &&
945                     _get_fun(DisposeImage, "GdipDisposeImage") &&
946                     _get_fun(GetImageDecoders, "GdipGetImageDecoders") &&
947                     _get_fun(GetImageDecodersSize, "GdipGetImageDecodersSize") &&
948                     _get_fun(GetImageEncoders, "GdipGetImageEncoders") &&
949                     _get_fun(GetImageEncodersSize, "GdipGetImageEncodersSize") &&
950                     _get_fun(SaveImageToFile, "GdipSaveImageToFile") &&
951                     _get_fun(SaveImageToStream, "GdipSaveImageToStream"))
952                 {
953                     using namespace Gdiplus;
954                     GdiplusStartupInput input;
955                     GdiplusStartupOutput output;
956                     Startup(&m_dwToken, &input, &output);
957                 }
958             }
959 
960             bool ret = (m_dwToken != 0);
961             ::LeaveCriticalSection(&m_sect);
962 
963             return ret;
964         }
965 
ReleaseGDIPlus()966         void ReleaseGDIPlus() noexcept
967         {
968             ::EnterCriticalSection(&m_sect);
969             if (m_dwToken)
970             {
971                 if (Shutdown)
972                     Shutdown(m_dwToken);
973 
974                 m_dwToken = 0;
975             }
976             if (m_hInst)
977             {
978                 ::FreeLibrary(m_hInst);
979                 m_hInst = NULL;
980             }
981             _clear_funs();
982             ::LeaveCriticalSection(&m_sect);
983         }
984 
IncreaseCImageCount()985         void IncreaseCImageCount() noexcept
986         {
987             ::EnterCriticalSection(&m_sect);
988             ++m_nCImageObjects;
989             ::LeaveCriticalSection(&m_sect);
990         }
991 
DecreaseCImageCount()992         void DecreaseCImageCount() noexcept
993         {
994             ::EnterCriticalSection(&m_sect);
995             if (--m_nCImageObjects == 0)
996             {
997                 ReleaseGDIPlus();
998             }
999             ::LeaveCriticalSection(&m_sect);
1000         }
1001     };
1002 
1003     static CInitGDIPlus s_gdiplus;
1004 
InitGDIPlus()1005     static bool InitGDIPlus() noexcept
1006     {
1007         return s_gdiplus.Init();
1008     }
1009 
1010 private:
1011     HBITMAP             m_hBitmap               = NULL;
1012     mutable HBITMAP     m_hOldBitmap            = NULL;
1013     INT                 m_nWidth                = 0;
1014     INT                 m_nHeight               = 0;
1015     INT                 m_nPitch                = 0;
1016     INT                 m_nBPP                  = 0;
1017     LPVOID              m_pBits                 = NULL;
1018     BOOL                m_bHasAlphaChannel      = FALSE;
1019     BOOL                m_bIsDIBSection         = FALSE;
1020     COLORREF            m_clrTransparentColor   = CLR_INVALID;
1021     mutable HDC         m_hDC                   = NULL;
1022     mutable LONG        m_nDCRefCount           = 0;
1023 
GetFileExtension(LPCWSTR pszFileName)1024     LPCWSTR GetFileExtension(LPCWSTR pszFileName) const
1025     {
1026         LPCWSTR pch = wcsrchr(pszFileName, L'\\');
1027         if (pch == NULL)
1028             pch = wcsrchr(pszFileName, L'/');
1029         pch = (pch ? wcsrchr(pch, L'.') : wcsrchr(pszFileName, L'.'));
1030         return (pch ? pch : (pszFileName + ::lstrlenW(pszFileName)));
1031     }
1032 
RGBFromPaletteIndex(int iIndex)1033     COLORREF RGBFromPaletteIndex(int iIndex) const
1034     {
1035         RGBQUAD table[256];
1036         GetColorTable(0, 256, table);
1037         RGBQUAD& quad =  table[iIndex];
1038         return RGB(quad.rgbRed, quad.rgbGreen, quad.rgbBlue);
1039     }
1040 
1041     static CLSID
FindCodecForExtension(LPCTSTR dotext,const Gdiplus::ImageCodecInfo * pCodecs,UINT nCodecs)1042     FindCodecForExtension(LPCTSTR dotext, const Gdiplus::ImageCodecInfo *pCodecs, UINT nCodecs)
1043     {
1044         for (UINT i = 0; i < nCodecs; ++i)
1045         {
1046             CString strSpecs(pCodecs[i].FilenameExtension);
1047             int ichOld = 0, ichSep;
1048             for (;;)
1049             {
1050                 ichSep = strSpecs.Find(TEXT(';'), ichOld);
1051 
1052                 CString strSpec;
1053                 if (ichSep < 0)
1054                     strSpec = strSpecs.Mid(ichOld);
1055                 else
1056                     strSpec = strSpecs.Mid(ichOld, ichSep - ichOld);
1057 
1058                 int ichDot = strSpec.ReverseFind(TEXT('.'));
1059                 if (ichDot >= 0)
1060                     strSpec = strSpec.Mid(ichDot);
1061 
1062                 if (!dotext || strSpec.CompareNoCase(dotext) == 0)
1063                     return pCodecs[i].Clsid;
1064 
1065                 if (ichSep < 0)
1066                     break;
1067 
1068                 ichOld = ichSep + 1;
1069             }
1070         }
1071         return CLSID_NULL;
1072     }
1073 
1074     static CLSID
FindCodecForFileType(REFGUID guidFileType,const Gdiplus::ImageCodecInfo * pCodecs,UINT nCodecs)1075     FindCodecForFileType(REFGUID guidFileType, const Gdiplus::ImageCodecInfo *pCodecs, UINT nCodecs)
1076     {
1077         for (UINT iInfo = 0; iInfo < nCodecs; ++iInfo)
1078         {
1079             if (::IsEqualGUID(pCodecs[iInfo].FormatID, guidFileType))
1080                 return pCodecs[iInfo].Clsid;
1081         }
1082         return CLSID_NULL;
1083     }
1084 
_getAllEncoders(UINT & cEncoders)1085     static Gdiplus::ImageCodecInfo* _getAllEncoders(UINT& cEncoders)
1086     {
1087         UINT total_size = 0;
1088         s_gdiplus.GetImageEncodersSize(&cEncoders, &total_size);
1089         if (total_size == 0)
1090             return NULL;  // failure
1091 
1092         Gdiplus::ImageCodecInfo *ret;
1093         ret = new Gdiplus::ImageCodecInfo[total_size / sizeof(ret[0])];
1094         if (ret == NULL)
1095         {
1096             cEncoders = 0;
1097             return NULL;  // failure
1098         }
1099 
1100         s_gdiplus.GetImageEncoders(cEncoders, total_size, ret);
1101         return ret; // needs delete[]
1102     }
1103 
_getAllDecoders(UINT & cDecoders)1104     static Gdiplus::ImageCodecInfo* _getAllDecoders(UINT& cDecoders)
1105     {
1106         UINT total_size = 0;
1107         s_gdiplus.GetImageDecodersSize(&cDecoders, &total_size);
1108         if (total_size == 0)
1109             return NULL;  // failure
1110 
1111         Gdiplus::ImageCodecInfo *ret;
1112         ret = new Gdiplus::ImageCodecInfo[total_size / sizeof(ret[0])];
1113         if (ret == NULL)
1114         {
1115             cDecoders = 0;
1116             return NULL;  // failure
1117         }
1118 
1119         s_gdiplus.GetImageDecoders(cDecoders, total_size, ret);
1120         return ret; // needs delete[]
1121     }
1122 
AttachInternal(HBITMAP hBitmap,DIBOrientation eOrientation,LONG iTransColor)1123     void AttachInternal(HBITMAP hBitmap, DIBOrientation eOrientation,
1124                         LONG iTransColor) noexcept
1125     {
1126         Destroy();
1127 
1128         DIBSECTION ds;
1129         BITMAP& bm = ds.dsBm;
1130 
1131         m_bIsDIBSection = (::GetObjectW(hBitmap, sizeof(ds), &ds) == sizeof(ds));
1132 
1133         if (!m_bIsDIBSection && !::GetObjectW(hBitmap, sizeof(bm), &bm))
1134             return;
1135 
1136         m_hBitmap = hBitmap;
1137         m_nWidth = bm.bmWidth;
1138         m_nHeight = bm.bmHeight;
1139         m_nBPP = bm.bmBitsPixel;
1140         m_bHasAlphaChannel = (bm.bmBitsPixel == 32);
1141         m_clrTransparentColor = CLR_INVALID;
1142 
1143         m_nPitch = 0;
1144         m_pBits = NULL;
1145         if (m_bIsDIBSection)
1146         {
1147             if (eOrientation == DIBOR_DEFAULT)
1148                 eOrientation = ((ds.dsBmih.biHeight < 0) ? DIBOR_TOPDOWN : DIBOR_BOTTOMUP);
1149 
1150             LPBYTE pb = (LPBYTE)bm.bmBits;
1151             if (eOrientation == DIBOR_BOTTOMUP)
1152             {
1153                 m_nPitch = -bm.bmWidthBytes;
1154                 m_pBits = pb + bm.bmWidthBytes * (m_nHeight - 1);
1155             }
1156             else
1157             {
1158                 m_nPitch = bm.bmWidthBytes;
1159                 m_pBits = pb;
1160             }
1161         }
1162     }
1163 
1164     BOOL CreateInternal(int nWidth, int nHeight, int nBPP,
1165                         DWORD eCompression, const DWORD* pdwBitmasks = NULL,
1166                         DWORD dwFlags = 0) noexcept
1167     {
1168         ATLASSERT(nWidth != 0);
1169         ATLASSERT(nHeight != 0);
1170 
1171         // initialize BITMAPINFO extension
1172         MYBITMAPINFOEX bi;
1173         ZeroMemory(&bi, sizeof(bi));
1174         bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1175         bi.bmiHeader.biWidth = nWidth;
1176         bi.bmiHeader.biHeight = nHeight;
1177         bi.bmiHeader.biPlanes = 1;
1178         bi.bmiHeader.biBitCount = nBPP;
1179         bi.bmiHeader.biCompression = eCompression;
1180 
1181         // is there alpha channel?
1182         bool bHasAlphaCh;
1183         bHasAlphaCh = (nBPP == 32 && (dwFlags & createAlphaChannel));
1184 
1185         // get orientation
1186         DIBOrientation eOrientation;
1187         eOrientation = ((nHeight > 0) ? DIBOR_BOTTOMUP : DIBOR_TOPDOWN);
1188 
1189         // does it have bit fields?
1190         if (eCompression == BI_BITFIELDS)
1191         {
1192             if (nBPP == 16 || nBPP == 32)
1193             {
1194                 // store the mask data
1195                 LPDWORD pdwMask = reinterpret_cast<LPDWORD>(bi.bmiColors);
1196                 pdwMask[0] = pdwBitmasks[0];
1197                 pdwMask[1] = pdwBitmasks[1];
1198                 pdwMask[2] = pdwBitmasks[2];
1199             }
1200             else
1201             {
1202                 return FALSE;
1203             }
1204         }
1205         else
1206         {
1207             ATLASSERT(pdwBitmasks == NULL);
1208             if (pdwBitmasks)
1209                 return FALSE;
1210         }
1211 
1212         // create a DIB section
1213         HDC hDC = ::CreateCompatibleDC(NULL);
1214         ATLASSERT(hDC);
1215         HBITMAP hbm = ::CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, NULL, NULL, 0);
1216         ATLASSERT(hbm);
1217         ::DeleteDC(hDC);
1218 
1219         // attach it
1220         AttachInternal(hbm, eOrientation, -1);
1221         m_bHasAlphaChannel = bHasAlphaCh;
1222 
1223         return hbm != NULL;
1224     }
1225 
1226 private:
1227     CImage(const CImage&) = delete;
1228     CImage& operator=(const CImage&) = delete;
1229 };
1230 
1231 DECLSPEC_SELECTANY CImage::CInitGDIPlus CImage::s_gdiplus;
1232 
1233 class CImageDC
1234 {
1235 private:
1236     const CImage& m_image;
1237     HDC m_hDC;
1238 
1239 public:
CImageDC(const CImage & image)1240     CImageDC(const CImage& image)
1241         : m_image(image)
1242         , m_hDC(image.GetDC())
1243     {
1244     }
1245 
~CImageDC()1246     virtual ~CImageDC() noexcept
1247     {
1248         m_image.ReleaseDC();
1249     }
1250 
HDC()1251     operator HDC() const noexcept
1252     {
1253         return m_hDC;
1254     }
1255 
1256     CImageDC(const CImageDC&) = delete;
1257     CImageDC& operator=(const CImageDC&) = delete;
1258 };
1259 
1260 } // namespace ATL
1261 
1262 #endif
1263 
1264 #ifndef _ATL_NO_AUTOMATIC_NAMESPACE
1265 using namespace ATL;
1266 #endif //!_ATL_NO_AUTOMATIC_NAMESPACE
1267