xref: /reactos/sdk/lib/atl/atlimage.h (revision 6a6b5ec2)
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 
40     CImage() noexcept
41     {
42         s_gdiplus.IncreaseCImageCount();
43     }
44 
45     virtual ~CImage() noexcept
46     {
47         Destroy();
48         s_gdiplus.DecreaseCImageCount();
49     }
50 
51     operator HBITMAP() noexcept
52     {
53         return m_hBitmap;
54     }
55 
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 
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 
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 
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 
204     void Destroy() noexcept
205     {
206         if (m_hBitmap)
207         {
208             ::DeleteObject(Detach());
209         }
210     }
211 
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     }
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     }
246     BOOL Draw(HDC hDestDC, int xDest, int yDest) const noexcept
247     {
248         return Draw(hDestDC, xDest, yDest, GetWidth(), GetHeight());
249     }
250     BOOL Draw(HDC hDestDC, const POINT& pointDest) const noexcept
251     {
252         return Draw(hDestDC, pointDest.x, pointDest.y);
253     }
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     }
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 
267     void *GetBits() noexcept
268     {
269         ATLASSERT(IsDIBSection());
270         return m_pBits;
271     }
272 
273     const void *GetBits() const noexcept
274     {
275         return const_cast<CImage*>(this)->GetBits();
276     }
277 
278     int GetBPP() const noexcept
279     {
280         ATLASSERT(m_hBitmap);
281         return m_nBPP;
282     }
283 
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 
293     int GetHeight() const noexcept
294     {
295         ATLASSERT(m_hBitmap);
296         return m_nHeight;
297     }
298 
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 
313     int GetPitch() const noexcept
314     {
315         ATLASSERT(IsDIBSection());
316         return m_nPitch;
317     }
318 
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 
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 
336     const void* GetPixelAddress(int x, int y) const noexcept
337     {
338         return const_cast<CImage*>(this)->GetPixelAddress(x, y);
339     }
340 
341     COLORREF GetTransparentColor() const noexcept
342     {
343         return m_clrTransparentColor;
344     }
345 
346     int GetWidth() const noexcept
347     {
348         ATLASSERT(m_hBitmap);
349         return m_nWidth;
350     }
351 
352     bool IsDIBSection() const noexcept
353     {
354         ATLASSERT(m_hBitmap);
355         return m_bIsDIBSection;
356     }
357 
358     bool IsIndexed() const noexcept
359     {
360         ATLASSERT(IsDIBSection());
361         return GetBPP() <= 8;
362     }
363 
364     bool IsNull() const noexcept
365     {
366         return m_hBitmap == NULL;
367     }
368 
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     }
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
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     }
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     }
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 
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 
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 
584     void SetPixel(int x, int y, COLORREF color) noexcept
585     {
586         GetDC();
587         ::SetPixelV(m_hDC, x, y, color);
588         ReleaseDC();
589     }
590 
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 
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 
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 
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:
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 
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 
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>
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 
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 
923         ~CInitGDIPlus() noexcept
924         {
925             ReleaseGDIPlus();
926             ::DeleteCriticalSection(&m_sect);
927         }
928 
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 
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 
985         void IncreaseCImageCount() noexcept
986         {
987             ::EnterCriticalSection(&m_sect);
988             ++m_nCImageObjects;
989             ::LeaveCriticalSection(&m_sect);
990         }
991 
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 
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 
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 
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
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
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 
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 
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 
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:
1240     CImageDC(const CImage& image)
1241         : m_image(image)
1242         , m_hDC(image.GetDC())
1243     {
1244     }
1245 
1246     virtual ~CImageDC() noexcept
1247     {
1248         m_image.ReleaseDC();
1249     }
1250 
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