xref: /reactos/sdk/lib/atl/atlimage.h (revision c2c66aff)
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         // TODO: use a string class
381 #ifdef UNICODE
382         LPCWSTR pszNameW = pszFileName;
383 #else
384         WCHAR szPath[MAX_PATH];
385         ::MultiByteToWideChar(CP_ACP, 0, pszFileName, -1, szPath, MAX_PATH);
386         LPCWSTR pszNameW = szPath;
387 #endif
388 
389         // create a GpBitmap object from file
390         using namespace Gdiplus;
391         GpBitmap *pBitmap = NULL;
392         GetCommon().CreateBitmapFromFile(pszNameW, &pBitmap);
393         ATLASSERT(pBitmap);
394 
395         // TODO & FIXME: get parameters (m_rgbTransColor etc.)
396 
397         // get bitmap handle
398         HBITMAP hbm = NULL;
399         Color color(0xFF, 0xFF, 0xFF);
400         Gdiplus::Status status;
401         status = GetCommon().CreateHBITMAPFromBitmap(
402             pBitmap, &hbm, color.GetValue());
403 
404         // delete GpBitmap
405         GetCommon().DisposeImage(pBitmap);
406 
407         // attach it
408         if (status == Ok)
409             Attach(hbm);
410         return (status == Ok ? S_OK : E_FAIL);
411     }
412     HRESULT Load(IStream* pStream) throw()
413     {
414         // create GpBitmap from stream
415         using namespace Gdiplus;
416         GpBitmap *pBitmap = NULL;
417         GetCommon().CreateBitmapFromStream(pStream, &pBitmap);
418         ATLASSERT(pBitmap);
419 
420         // TODO & FIXME: get parameters (m_rgbTransColor etc.)
421 
422         // get bitmap handle
423         HBITMAP hbm = NULL;
424         Color color(0xFF, 0xFF, 0xFF);
425         Gdiplus::Status status;
426         status = GetCommon().CreateHBITMAPFromBitmap(
427             pBitmap, &hbm, color.GetValue());
428 
429         // delete Bitmap
430         GetCommon().DisposeImage(pBitmap);
431 
432         // attach it
433         if (status == Ok)
434             Attach(hbm);
435         return (status == Ok ? S_OK : E_FAIL);
436     }
437 
438     // NOTE: LoadFromResource loads BITMAP resource only
439     void LoadFromResource(HINSTANCE hInstance, LPCTSTR pszResourceName) throw()
440     {
441         HANDLE hHandle = ::LoadImage(hInstance, pszResourceName,
442                                      IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
443         Attach(reinterpret_cast<HBITMAP>(hHandle));
444     }
445     void LoadFromResource(HINSTANCE hInstance, UINT nIDResource) throw()
446     {
447         LoadFromResource(hInstance, MAKEINTRESOURCE(nIDResource));
448     }
449 
450     BOOL MaskBlt(HDC hDestDC, int xDest, int yDest,
451                  int nDestWidth, int nDestHeight, int xSrc, int ySrc,
452                  HBITMAP hbmMask, int xMask, int yMask,
453                  DWORD dwROP = SRCCOPY) const throw()
454     {
455         ATLASSERT(IsTransparencySupported());
456         GetDC();
457         BOOL ret = ::MaskBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
458                              m_hDC, xSrc, ySrc,
459                              hbmMask, xMask, yMask, dwROP);
460         ReleaseDC();
461         return ret;
462     }
463     BOOL MaskBlt(HDC hDestDC, const RECT& rectDest, const POINT& pointSrc,
464                  HBITMAP hbmMask, const POINT& pointMask,
465                  DWORD dwROP = SRCCOPY) const throw()
466     {
467         return MaskBlt(hDestDC, rectDest.left, rectDest.top,
468             rectDest.right - rectDest.left, rectDest.bottom - rectDest.top,
469             pointSrc.x, pointSrc.y, hbmMask, pointMask.x, pointMask.y, dwROP);
470     }
471     BOOL MaskBlt(HDC hDestDC, int xDest, int yDest,
472                  HBITMAP hbmMask, DWORD dwROP = SRCCOPY) const throw()
473     {
474         return MaskBlt(hDestDC, xDest, yDest, GetWidth(), GetHeight(),
475                        0, 0, hbmMask, 0, 0, dwROP);
476     }
477     BOOL MaskBlt(HDC hDestDC, const POINT& pointDest,
478                  HBITMAP hbmMask, DWORD dwROP = SRCCOPY) const throw()
479     {
480         return MaskBlt(hDestDC, pointDest.x, pointDest.y, hbmMask, dwROP);
481     }
482 
483     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints,
484                 int xSrc, int ySrc, int nSrcWidth, int nSrcHeight,
485                 HBITMAP hbmMask = NULL,
486                 int xMask = 0, int yMask = 0) const throw()
487     {
488         ATLASSERT(IsTransparencySupported());
489         GetDC();
490         BOOL ret = ::PlgBlt(hDestDC, pPoints, m_hDC,
491                             xSrc, ySrc, nSrcWidth, nSrcHeight,
492                             hbmMask, xMask, yMask);
493         ReleaseDC();
494         return ret;
495     }
496     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints,
497                 HBITMAP hbmMask = NULL) const throw()
498     {
499         return PlgBlt(hDestDC, pPoints, 0, 0, GetWidth(), GetHeight(),
500                       hbmMask);
501     }
502     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints, const RECT& rectSrc,
503                 HBITMAP hbmMask, const POINT& pointMask) const throw()
504     {
505         return PlgBlt(hDestDC, pPoints, rectSrc.left, rectSrc.top,
506             rectSrc.right - rectSrc.left, rectSrc.bottom - rectSrc.top,
507             hbmMask, pointMask.x, pointMask.y);
508     }
509     BOOL PlgBlt(HDC hDestDC, const POINT* pPoints, const RECT& rectSrc,
510                 HBITMAP hbmMask = NULL) const throw()
511     {
512         POINT pointMask = {0, 0};
513         return PlgBlt(hDestDC, pPoints, rectSrc, hbmMask, pointMask);
514     }
515 
516     void ReleaseGDIPlus() throw()
517     {
518         COMMON*& pCommon = GetCommonPtr();
519         if (pCommon && pCommon->Release() == 0)
520         {
521             delete pCommon;
522             pCommon = NULL;
523         }
524     }
525 
526     HRESULT Save(IStream* pStream, GUID *guidFileType) const throw()
527     {
528         using namespace Gdiplus;
529         ATLASSERT(m_hbm);
530 
531         // TODO & FIXME: set parameters (m_rgbTransColor etc.)
532         CLSID clsid;
533         if (!GetClsidFromFileType(&clsid, guidFileType))
534             return E_FAIL;
535 
536         // create a GpBitmap from HBITMAP
537         GpBitmap *pBitmap = NULL;
538         GetCommon().CreateBitmapFromHBITMAP(m_hbm, NULL, &pBitmap);
539 
540         // save to stream
541         Status status;
542         status = GetCommon().SaveImageToStream(pBitmap, pStream, &clsid, NULL);
543 
544         // destroy GpBitmap
545         GetCommon().DisposeImage(pBitmap);
546 
547         return (status == Ok ? S_OK : E_FAIL);
548     }
549     HRESULT Save(LPCTSTR pszFileName,
550                  REFGUID guidFileType = GUID_NULL) const throw()
551     {
552         using namespace Gdiplus;
553         ATLASSERT(m_hbm);
554 
555         // TODO & FIXME: set parameters (m_rgbTransColor etc.)
556 
557         // convert the file name string into Unicode
558         // TODO: use a string class
559 #ifdef UNICODE
560         LPCWSTR pszNameW = pszFileName;
561 #else
562         WCHAR szPath[MAX_PATH];
563         ::MultiByteToWideChar(CP_ACP, 0, pszFileName, -1, szPath, MAX_PATH);
564         LPCWSTR pszNameW = szPath;
565 #endif
566 
567         // if the file type is null, get the file type from extension
568         const GUID *FileType = &guidFileType;
569         if (IsGuidEqual(guidFileType, GUID_NULL))
570         {
571             LPCWSTR pszExt = GetFileExtension(pszNameW);
572             FileType = FileTypeFromExtension(pszExt);
573         }
574 
575         // get CLSID from file type
576         CLSID clsid;
577         if (!GetClsidFromFileType(&clsid, FileType))
578             return E_FAIL;
579 
580         // create a GpBitmap from HBITMAP
581         GpBitmap *pBitmap = NULL;
582         GetCommon().CreateBitmapFromHBITMAP(m_hbm, NULL, &pBitmap);
583 
584         // save to file
585         Status status;
586         status = GetCommon().SaveImageToFile(pBitmap, pszNameW, &clsid, NULL);
587 
588         // destroy GpBitmap
589         GetCommon().DisposeImage(pBitmap);
590 
591         return (status == Ok ? S_OK : E_FAIL);
592     }
593 
594     void SetColorTable(UINT iFirstColor, UINT nColors,
595                        const RGBQUAD* prgbColors) throw()
596     {
597         ATLASSERT(IsDIBSection());
598         GetDC();
599         ::SetDIBColorTable(m_hDC, iFirstColor, nColors, prgbColors);
600         ReleaseDC();
601     }
602 
603     void SetPixel(int x, int y, COLORREF color) throw()
604     {
605         GetDC();
606         ::SetPixelV(m_hDC, x, y, color);
607         ReleaseDC();
608     }
609 
610     void SetPixelIndexed(int x, int y, int iIndex) throw()
611     {
612         ATLASSERT(IsIndexed());
613         GetDC();
614         ::SetPixelV(m_hDC, x, y, PALETTEINDEX(iIndex));
615         ReleaseDC();
616     }
617 
618     void SetPixelRGB(int x, int y, BYTE r, BYTE g, BYTE b) throw()
619     {
620         SetPixel(x, y, RGB(r, g, b));
621     }
622 
623     COLORREF SetTransparentColor(COLORREF rgbTransparent) throw()
624     {
625         ATLASSERT(m_hbm);
626         COLORREF rgbOldColor = m_rgbTransColor;
627         m_rgbTransColor = rgbTransparent;
628         return rgbOldColor;
629     }
630 
631     BOOL StretchBlt(HDC hDestDC, int xDest, int yDest,
632                     int nDestWidth, int nDestHeight,
633                     int xSrc, int ySrc, int nSrcWidth, int nSrcHeight,
634                     DWORD dwROP = SRCCOPY) const throw()
635     {
636         GetDC();
637         BOOL ret = ::StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
638                                 m_hDC, xSrc, ySrc, nSrcWidth, nSrcHeight, dwROP);
639         ReleaseDC();
640         return ret;
641     }
642     BOOL StretchBlt(HDC hDestDC, int xDest, int yDest,
643                     int nDestWidth, int nDestHeight,
644                     DWORD dwROP = SRCCOPY) const throw()
645     {
646         return StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
647                           0, 0, GetWidth(), GetHeight(), dwROP);
648     }
649     BOOL StretchBlt(HDC hDestDC, const RECT& rectDest,
650                     DWORD dwROP = SRCCOPY) const throw()
651     {
652         return StretchBlt(hDestDC, rectDest.left, rectDest.top,
653                           rectDest.right - rectDest.left,
654                           rectDest.bottom - rectDest.top, dwROP);
655     }
656     BOOL StretchBlt(HDC hDestDC, const RECT& rectDest,
657                     const RECT& rectSrc, DWORD dwROP = SRCCOPY) const throw()
658     {
659         return StretchBlt(hDestDC, rectDest.left, rectDest.top,
660                           rectDest.right - rectDest.left,
661                           rectDest.bottom - rectDest.top,
662                           rectSrc.left, rectSrc.top,
663                           rectSrc.right - rectSrc.left,
664                           rectSrc.bottom - rectSrc.top, dwROP);
665     }
666 
667     BOOL TransparentBlt(HDC hDestDC, int xDest, int yDest,
668                         int nDestWidth, int nDestHeight,
669                         int xSrc, int ySrc, int nSrcWidth, int nSrcHeight,
670                         UINT crTransparent = CLR_INVALID) const throw()
671     {
672         ATLASSERT(IsTransparencySupported());
673         GetDC();
674         BOOL ret = ::TransparentBlt(hDestDC, xDest, yDest,
675                                     nDestWidth, nDestHeight,
676                                     m_hDC, xSrc, ySrc,
677                                     nSrcWidth, nSrcHeight, crTransparent);
678         ReleaseDC();
679         return ret;
680     }
681     BOOL TransparentBlt(HDC hDestDC, int xDest, int yDest,
682                         int nDestWidth, int nDestHeight,
683                         UINT crTransparent = CLR_INVALID) const throw()
684     {
685         return TransparentBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight,
686                               0, 0, GetWidth(), GetHeight(), crTransparent);
687     }
688     BOOL TransparentBlt(HDC hDestDC, const RECT& rectDest,
689                         UINT crTransparent = CLR_INVALID) const throw()
690     {
691         return TransparentBlt(hDestDC, rectDest.left, rectDest.top,
692                               rectDest.right - rectDest.left,
693                               rectDest.bottom - rectDest.top, crTransparent);
694     }
695     BOOL TransparentBlt(
696        HDC hDestDC, const RECT& rectDest,
697        const RECT& rectSrc, UINT crTransparent = CLR_INVALID) const throw()
698     {
699         return TransparentBlt(hDestDC, rectDest.left, rectDest.top,
700             rectDest.right - rectDest.left, rectDest.bottom - rectDest.left,
701             rectSrc.left, rectSrc.top, rectSrc.right - rectSrc.left,
702             rectSrc.bottom - rectSrc.top, crTransparent);
703     }
704 
705 public:
706     static BOOL IsTransparencySupported() throw()
707     {
708         return TRUE;
709     }
710 
711     enum ExcludeFlags
712     {
713         excludeGIF          = 0x01,
714         excludeBMP          = 0x02,
715         excludeEMF          = 0x04,
716         excludeWMF          = 0x08,
717         excludeJPEG         = 0x10,
718         excludePNG          = 0x20,
719         excludeTIFF         = 0x40,
720         excludeIcon         = 0x80,
721         excludeOther        = 0x80000000,
722         excludeDefaultLoad  = 0,
723         excludeDefaultSave  = excludeIcon | excludeEMF | excludeWMF
724     };
725 
726     struct FILTER_DATA {
727         DWORD dwExclude;
728         const TCHAR *title;
729         const TCHAR *extensions;
730         const GUID *guid;
731     };
732 
733 protected:
734     static HRESULT GetCommonFilterString(
735         CSimpleString& strFilter,
736         CSimpleArray<GUID>& aguidFileTypes,
737         LPCTSTR pszAllFilesDescription,
738         DWORD dwExclude,
739         TCHAR chSeparator)
740     {
741         static const FILTER_DATA table[] =
742         {
743             {excludeBMP, TEXT("BMP"), TEXT("*.BMP;*.DIB;*.RLE"), &Gdiplus::ImageFormatBMP},
744             {excludeJPEG, TEXT("JPEG"), TEXT("*.JPG;*.JPEG;*.JPE;*.JFIF"), &Gdiplus::ImageFormatJPEG},
745             {excludeGIF, TEXT("GIF"), TEXT("*.GIF"), &Gdiplus::ImageFormatGIF},
746             {excludeEMF, TEXT("EMF"), TEXT("*.EMF"), &Gdiplus::ImageFormatEMF},
747             {excludeWMF, TEXT("WMF"), TEXT("*.WMF"), &Gdiplus::ImageFormatWMF},
748             {excludeTIFF, TEXT("TIFF"), TEXT("*.TIF;*.TIFF"), &Gdiplus::ImageFormatTIFF},
749             {excludePNG, TEXT("PNG"), TEXT("*.PNG"), &Gdiplus::ImageFormatPNG},
750             {excludeIcon, TEXT("ICO"), TEXT("*.ICO"), &Gdiplus::ImageFormatIcon}
751         };
752 
753         if (pszAllFilesDescription)
754         {
755             strFilter += pszAllFilesDescription;
756             strFilter += chSeparator;
757 
758             BOOL bFirst = TRUE;
759             for (size_t i = 0; i < _countof(table); ++i)
760             {
761                 if ((dwExclude & table[i].dwExclude) != 0)
762                     continue;
763 
764                 if (bFirst)
765                     bFirst = FALSE;
766                 else
767                     strFilter += TEXT(';');
768 
769                 strFilter += table[i].extensions;
770             }
771             strFilter += chSeparator;
772 
773             aguidFileTypes.Add(GUID_NULL);
774         }
775 
776         for (size_t i = 0; i < _countof(table); ++i)
777         {
778             if ((dwExclude & table[i].dwExclude) != 0)
779                 continue;
780             strFilter += table[i].title;
781             strFilter += TEXT(" (");
782             strFilter += table[i].extensions;
783             strFilter += TEXT(")");
784             strFilter += chSeparator;
785             strFilter += table[i].extensions;
786             strFilter += chSeparator;
787 
788             aguidFileTypes.Add(*table[i].guid);
789         }
790 
791         strFilter += chSeparator;
792 
793         return S_OK;
794     }
795 
796 public:
797     static HRESULT GetImporterFilterString(
798         CSimpleString& strImporters,
799         CSimpleArray<GUID>& aguidFileTypes,
800         LPCTSTR pszAllFilesDescription = NULL,
801         DWORD dwExclude = excludeDefaultLoad,
802         TCHAR chSeparator = TEXT('|'))
803     {
804         return GetCommonFilterString(strImporters,
805                                      aguidFileTypes,
806                                      pszAllFilesDescription,
807                                      dwExclude,
808                                      chSeparator);
809     }
810 
811     static HRESULT GetExporterFilterString(
812         CSimpleString& strExporters,
813         CSimpleArray<GUID>& aguidFileTypes,
814         LPCTSTR pszAllFilesDescription = NULL,
815         DWORD dwExclude = excludeDefaultSave,
816         TCHAR chSeparator = TEXT('|'))
817     {
818         return GetCommonFilterString(strExporters,
819                                      aguidFileTypes,
820                                      pszAllFilesDescription,
821                                      dwExclude,
822                                      chSeparator);
823     }
824 
825 protected:
826     // an extension of BITMAPINFO
827     struct MYBITMAPINFOEX
828     {
829         BITMAPINFOHEADER bmiHeader;
830         RGBQUAD bmiColors[256];
831         BITMAPINFO *get()
832         {
833             return reinterpret_cast<BITMAPINFO *>(this);
834         }
835         const BITMAPINFO *get() const
836         {
837             return reinterpret_cast<const BITMAPINFO *>(this);
838         }
839     };
840 
841     // The common data of atlimage
842     struct COMMON
843     {
844         // abbreviations of GDI+ basic types
845         typedef Gdiplus::GpStatus St;
846         typedef Gdiplus::ImageCodecInfo ICI;
847         typedef Gdiplus::GpBitmap Bm;
848         typedef Gdiplus::EncoderParameters EncParams;
849         typedef Gdiplus::GpImage Im;
850         typedef Gdiplus::ARGB ARGB;
851         typedef HBITMAP HBM;
852         typedef Gdiplus::GdiplusStartupInput GSI;
853         typedef Gdiplus::GdiplusStartupOutput GSO;
854 
855         // GDI+ function types
856 #undef API
857 #undef CST
858 #define API WINGDIPAPI
859 #define CST GDIPCONST
860         typedef St (WINAPI *STARTUP)(ULONG_PTR *, const GSI *, GSO *);
861         typedef void (WINAPI *SHUTDOWN)(ULONG_PTR);
862         typedef St (API *GETIMAGEENCODERSSIZE)(UINT *, UINT *);
863         typedef St (API *GETIMAGEENCODERS)(UINT, UINT, ICI *);
864         typedef St (API *CREATEBITMAPFROMFILE)(CST WCHAR*, Bm **);
865         typedef St (API *CREATEHBITMAPFROMBITMAP)(Bm *, HBM *, ARGB);
866         typedef St (API *CREATEBITMAPFROMSTREAM)(IStream *, Bm **);
867         typedef St (API *CREATEBITMAPFROMHBITMAP)(HBM, HPALETTE, Bm **);
868         typedef St (API *SAVEIMAGETOSTREAM)(Im *, IStream *, CST CLSID *,
869                                             CST EncParams *);
870         typedef St (API *SAVEIMAGETOFILE)(Im *, CST WCHAR *, CST CLSID *,
871                                           CST EncParams *);
872         typedef St (API *DISPOSEIMAGE)(Im*);
873 #undef API
874 #undef CST
875 
876         // members
877         int                     count;
878         HINSTANCE               hinstGdiPlus;
879         ULONG_PTR               gdiplusToken;
880 
881         // GDI+ functions
882         STARTUP                 Startup;
883         SHUTDOWN                Shutdown;
884         GETIMAGEENCODERSSIZE    GetImageEncodersSize;
885         GETIMAGEENCODERS        GetImageEncoders;
886         CREATEBITMAPFROMFILE    CreateBitmapFromFile;
887         CREATEHBITMAPFROMBITMAP CreateHBITMAPFromBitmap;
888         CREATEBITMAPFROMSTREAM  CreateBitmapFromStream;
889         CREATEBITMAPFROMHBITMAP CreateBitmapFromHBITMAP;
890         SAVEIMAGETOSTREAM       SaveImageToStream;
891         SAVEIMAGETOFILE         SaveImageToFile;
892         DISPOSEIMAGE            DisposeImage;
893 
894         COMMON()
895         {
896             count = 0;
897             hinstGdiPlus = NULL;
898             Startup = NULL;
899             Shutdown = NULL;
900             GetImageEncodersSize = NULL;
901             GetImageEncoders = NULL;
902             CreateBitmapFromFile = NULL;
903             CreateHBITMAPFromBitmap = NULL;
904             CreateBitmapFromStream = NULL;
905             CreateBitmapFromHBITMAP = NULL;
906             SaveImageToStream = NULL;
907             SaveImageToFile = NULL;
908             DisposeImage = NULL;
909         }
910         ~COMMON()
911         {
912             FreeLib();
913         }
914 
915         ULONG AddRef()
916         {
917             return ++count;
918         }
919         ULONG Release()
920         {
921             return --count;
922         }
923 
924         // get procedure address of the DLL
925         template <typename TYPE>
926         TYPE AddrOf(const char *name)
927         {
928             FARPROC proc = ::GetProcAddress(hinstGdiPlus, name);
929             return reinterpret_cast<TYPE>(proc);
930         }
931 
932         HINSTANCE LoadLib()
933         {
934             if (hinstGdiPlus)
935                 return hinstGdiPlus;
936 
937             hinstGdiPlus = ::LoadLibraryA("gdiplus.dll");
938 
939             // get procedure addresses from the DLL
940             Startup = AddrOf<STARTUP>("GdiplusStartup");
941             Shutdown = AddrOf<SHUTDOWN>("GdiplusShutdown");
942             GetImageEncodersSize =
943                 AddrOf<GETIMAGEENCODERSSIZE>("GdipGetImageEncodersSize");
944             GetImageEncoders = AddrOf<GETIMAGEENCODERS>("GdipGetImageEncoders");
945             CreateBitmapFromFile =
946                 AddrOf<CREATEBITMAPFROMFILE>("GdipCreateBitmapFromFile");
947             CreateHBITMAPFromBitmap =
948                 AddrOf<CREATEHBITMAPFROMBITMAP>("GdipCreateHBITMAPFromBitmap");
949             CreateBitmapFromStream =
950                 AddrOf<CREATEBITMAPFROMSTREAM>("GdipCreateBitmapFromStream");
951             CreateBitmapFromHBITMAP =
952                 AddrOf<CREATEBITMAPFROMHBITMAP>("GdipCreateBitmapFromHBITMAP");
953             SaveImageToStream =
954                 AddrOf<SAVEIMAGETOSTREAM>("GdipSaveImageToStream");
955             SaveImageToFile = AddrOf<SAVEIMAGETOFILE>("GdipSaveImageToFile");
956             DisposeImage = AddrOf<DISPOSEIMAGE>("GdipDisposeImage");
957 
958             if (hinstGdiPlus && Startup)
959             {
960                 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
961                 Startup(&gdiplusToken, &gdiplusStartupInput, NULL);
962             }
963 
964             return hinstGdiPlus;
965         }
966         void FreeLib()
967         {
968             if (hinstGdiPlus)
969             {
970                 Shutdown(gdiplusToken);
971 
972                 Startup = NULL;
973                 Shutdown = NULL;
974                 GetImageEncodersSize = NULL;
975                 GetImageEncoders = NULL;
976                 CreateBitmapFromFile = NULL;
977                 CreateHBITMAPFromBitmap = NULL;
978                 CreateBitmapFromStream = NULL;
979                 CreateBitmapFromHBITMAP = NULL;
980                 SaveImageToStream = NULL;
981                 SaveImageToFile = NULL;
982                 DisposeImage = NULL;
983                 ::FreeLibrary(hinstGdiPlus);
984                 hinstGdiPlus = NULL;
985             }
986         }
987     }; // struct COMMON
988 
989     static COMMON*& GetCommonPtr()
990     {
991         static COMMON *s_pCommon = NULL;
992         return s_pCommon;
993     }
994 
995     static COMMON& GetCommon()
996     {
997         COMMON*& pCommon = GetCommonPtr();
998         if (pCommon == NULL)
999             pCommon = new COMMON;
1000         return *pCommon;
1001     }
1002 
1003 protected:
1004     HBITMAP             m_hbm;
1005     mutable HGDIOBJ     m_hbmOld;
1006     mutable HDC         m_hDC;
1007     DIBOrientation      m_eOrientation;
1008     bool                m_bHasAlphaCh;
1009     bool                m_bIsDIBSec;
1010     COLORREF            m_rgbTransColor;
1011     union
1012     {
1013         BITMAP          m_bm;
1014         DIBSECTION      m_ds;
1015     };
1016 
1017     LPCWSTR GetFileExtension(LPCWSTR pszFileName) const
1018     {
1019         LPCWSTR pch = wcsrchr(pszFileName, L'\\');
1020         if (pch == NULL)
1021             pch = wcsrchr(pszFileName, L'/');
1022         pch = (pch ? wcsrchr(pch, L'.') : wcsrchr(pszFileName, L'.'));
1023         return (pch ? pch : (pszFileName + ::lstrlenW(pszFileName)));
1024     }
1025 
1026     COLORREF RGBFromPaletteIndex(int iIndex) const
1027     {
1028         RGBQUAD table[256];
1029         GetColorTable(0, 256, table);
1030         RGBQUAD& quad =  table[iIndex];
1031         return RGB(quad.rgbRed, quad.rgbGreen, quad.rgbBlue);
1032     }
1033 
1034     struct EXTENSION_ENTRY
1035     {
1036         LPCWSTR pszExt;
1037         GUID guid;
1038     };
1039 
1040     const GUID *FileTypeFromExtension(LPCWSTR pszExt) const
1041     {
1042         static const EXTENSION_ENTRY table[] =
1043         {
1044             {L".jpg", Gdiplus::ImageFormatJPEG},
1045             {L".png", Gdiplus::ImageFormatPNG},
1046             {L".bmp", Gdiplus::ImageFormatBMP},
1047             {L".gif", Gdiplus::ImageFormatGIF},
1048             {L".tif", Gdiplus::ImageFormatTIFF},
1049             {L".jpeg", Gdiplus::ImageFormatJPEG},
1050             {L".jpe", Gdiplus::ImageFormatJPEG},
1051             {L".jfif", Gdiplus::ImageFormatJPEG},
1052             {L".dib", Gdiplus::ImageFormatBMP},
1053             {L".rle", Gdiplus::ImageFormatBMP},
1054             {L".tiff", Gdiplus::ImageFormatTIFF}
1055         };
1056         const size_t count = _countof(table);
1057         for (size_t i = 0; i < count; ++i)
1058         {
1059             if (::lstrcmpiW(table[i].pszExt, pszExt) == 0)
1060                 return &table[i].guid;
1061         }
1062         return NULL;
1063     }
1064 
1065     struct FORMAT_ENTRY
1066     {
1067         GUID guid;
1068         LPCWSTR mime;
1069     };
1070 
1071     bool GetClsidFromFileType(CLSID *clsid, const GUID *guid) const
1072     {
1073         static const FORMAT_ENTRY table[] =
1074         {
1075             {Gdiplus::ImageFormatJPEG, L"image/jpeg"},
1076             {Gdiplus::ImageFormatPNG, L"image/png"},
1077             {Gdiplus::ImageFormatBMP, L"image/bmp"},
1078             {Gdiplus::ImageFormatGIF, L"image/gif"},
1079             {Gdiplus::ImageFormatTIFF, L"image/tiff"}
1080         };
1081         const size_t count = _countof(table);
1082         for (size_t i = 0; i < count; ++i)
1083         {
1084             if (IsGuidEqual(table[i].guid, *guid))
1085             {
1086                 int num = GetEncoderClsid(table[i].mime, clsid);
1087                 if (num >= 0)
1088                 {
1089                     return true;
1090                 }
1091             }
1092         }
1093         return false;
1094     }
1095 
1096     int GetEncoderClsid(LPCWSTR mime, CLSID *clsid) const
1097     {
1098         UINT count = 0, total_size = 0;
1099         GetCommon().GetImageEncodersSize(&count, &total_size);
1100         if (total_size == 0)
1101             return -1;  // failure
1102 
1103         Gdiplus::ImageCodecInfo *pInfo;
1104         BYTE *pb = new BYTE[total_size];
1105         ATLASSERT(pb);
1106         pInfo = reinterpret_cast<Gdiplus::ImageCodecInfo *>(pb);
1107         if (pInfo == NULL)
1108             return -1;  // failure
1109 
1110         GetCommon().GetImageEncoders(count, total_size, pInfo);
1111 
1112         for (UINT iInfo = 0; iInfo < count; ++iInfo)
1113         {
1114             if (::lstrcmpiW(pInfo[iInfo].MimeType, mime) == 0)
1115             {
1116                 *clsid = pInfo[iInfo].Clsid;
1117                 delete[] pb;
1118                 return iInfo;  // success
1119             }
1120         }
1121 
1122         delete[] pb;
1123         return -1;  // failure
1124     }
1125 
1126     bool IsGuidEqual(const GUID& guid1, const GUID& guid2) const
1127     {
1128         RPC_STATUS status;
1129         if (::UuidEqual(const_cast<GUID *>(&guid1),
1130                         const_cast<GUID *>(&guid2), &status))
1131         {
1132             if (status == RPC_S_OK)
1133                 return true;
1134         }
1135         return false;
1136     }
1137 
1138     void AttachInternal(HBITMAP hBitmap, DIBOrientation eOrientation,
1139                         LONG iTransColor)
1140     {
1141         Destroy();
1142 
1143         const int size = sizeof(DIBSECTION);
1144         m_bIsDIBSec = (::GetObject(hBitmap, size, &m_ds) == size);
1145 
1146         bool bOK = (::GetObject(hBitmap, sizeof(BITMAP), &m_bm) != 0);
1147 
1148         if (bOK)
1149         {
1150             m_hbm = hBitmap;
1151             m_eOrientation = eOrientation;
1152             m_bHasAlphaCh = (m_bm.bmBitsPixel == 32);
1153             m_rgbTransColor = CLR_INVALID;
1154         }
1155     }
1156 
1157     BOOL CreateInternal(int nWidth, int nHeight, int nBPP,
1158                         DWORD eCompression, const DWORD* pdwBitmasks = NULL,
1159                         DWORD dwFlags = 0) throw()
1160     {
1161         ATLASSERT(nWidth != 0);
1162         ATLASSERT(nHeight != 0);
1163 
1164         // initialize BITMAPINFO extension
1165         MYBITMAPINFOEX bi;
1166         ZeroMemory(&bi, sizeof(bi));
1167         bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1168         bi.bmiHeader.biWidth = nWidth;
1169         bi.bmiHeader.biHeight = nHeight;
1170         bi.bmiHeader.biPlanes = 1;
1171         bi.bmiHeader.biBitCount = nBPP;
1172         bi.bmiHeader.biCompression = eCompression;
1173 
1174         // is there alpha channel?
1175         bool bHasAlphaCh;
1176         bHasAlphaCh = (nBPP == 32 && (dwFlags & createAlphaChannel));
1177 
1178         // get orientation
1179         DIBOrientation eOrientation;
1180         eOrientation = ((nHeight > 0) ? DIBOR_BOTTOMUP : DIBOR_TOPDOWN);
1181 
1182         // does it have bit fields?
1183         if (eCompression == BI_BITFIELDS)
1184         {
1185             if (nBPP == 16 || nBPP == 32)
1186             {
1187                 // store the mask data
1188                 LPDWORD pdwMask = reinterpret_cast<LPDWORD>(bi.bmiColors);
1189                 pdwMask[0] = pdwBitmasks[0];
1190                 pdwMask[1] = pdwBitmasks[1];
1191                 pdwMask[2] = pdwBitmasks[2];
1192             }
1193             else
1194             {
1195                 return FALSE;
1196             }
1197         }
1198         else
1199         {
1200             ATLASSERT(pdwBitmasks == NULL);
1201             if (pdwBitmasks)
1202                 return FALSE;
1203         }
1204 
1205         // create a DIB section
1206         HDC hDC = ::CreateCompatibleDC(NULL);
1207         ATLASSERT(hDC);
1208         LPVOID pvBits;
1209         HBITMAP hbm = ::CreateDIBSection(hDC, bi.get(), DIB_RGB_COLORS,
1210                                          &pvBits, NULL, 0);
1211         ATLASSERT(hbm);
1212         ::DeleteDC(hDC);
1213 
1214         // attach it
1215         AttachInternal(hbm, eOrientation, -1);
1216         m_bHasAlphaCh = bHasAlphaCh;
1217 
1218         return hbm != NULL;
1219     }
1220 
1221 private:
1222     // NOTE: CImage is not copyable
1223     CImage(const CImage&);
1224     CImage& operator=(const CImage&);
1225 };
1226 
1227 }
1228 
1229 #endif
1230 
1231 #ifndef _ATL_NO_AUTOMATIC_NAMESPACE
1232 using namespace ATL;
1233 #endif //!_ATL_NO_AUTOMATIC_NAMESPACE
1234