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