[ATL] Add CHAIN_MSG_MAP macro
[reactos.git] / sdk / lib / atl / atlimage.h
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