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