@joebigelow / wix-1 / commits / c661b773

In thmutil when drawing images, use GDI+ and the actual dimensions.

Partial fix for #6155 Fixes #4106

Sean Hall committed Jun 3, 2021 at 18:32 UTC c661b773f8ae37bbdea6da25a57d3626e7113920
7 files changed +259 -97
src/api/burn/balutil/precomp.h
+7
@@ -3,6 +3,12 @@
3
4
5 #include <windows.h>
6 +
7 +#pragma warning(push)
8 +#pragma warning(disable:4458) // declaration of 'xxx' hides class member
9 +#include <gdiplus.h>
10 +#pragma warning(pop)
11 +
12 #include <bitsmsg.h>
13 #include <msi.h>
14 #include <wininet.h>
@@ -13,6 +19,7 @@
19 #include <pathutil.h>
20 #include <locutil.h>
21 #include <memutil.h>
22 +#include <dictutil.h>
23 #include <strutil.h>
24 #include <thmutil.h>
25 #include <xmlutil.h>
src/api/burn/test/BalUtilUnitTest/precomp.h
+7
@@ -3,10 +3,17 @@
3
4
5 #include <windows.h>
6 +
7 +#pragma warning(push)
8 +#pragma warning(disable:4458) // declaration of 'xxx' hides class member
9 +#include <gdiplus.h>
10 +#pragma warning(pop)
11 +
12 #include <msiquery.h>
13 #include <CommCtrl.h>
14
15 #include <dutil.h>
16 +#include <dictutil.h>
17 #include <locutil.h>
18 #include <thmutil.h>
19
src/ext/Bal/Samples/bafunctions/precomp.h
+1 -1
@@ -20,7 +20,7 @@
20 // Standard WiX header files, include as required
21 #include "dutil.h"
22 //#include "memutil.h"
23 -//#include "dictutil.h"
23 +#include "dictutil.h"
24 //#include "dirutil.h"
25 #include "fileutil.h"
26 #include "locutil.h"
src/libs/dutil/WixToolset.DUtil/gdiputil.cpp
+24 -1
@@ -56,6 +56,7 @@ extern "C" void DAPI GdipUninitialize(
56
57 /********************************************************************
58 GdipBitmapFromResource - read a GDI+ image out of a resource stream
59 + of type RT_RCDATA.
60
61 ********************************************************************/
62 extern "C" HRESULT DAPI GdipBitmapFromResource(
@@ -67,12 +68,13 @@ extern "C" HRESULT DAPI GdipBitmapFromResource(
68 HRESULT hr = S_OK;
69 LPVOID pvData = NULL;
70 DWORD cbData = 0;
70 - HGLOBAL hGlobal = NULL;;
71 + HGLOBAL hGlobal = NULL;
72 LPVOID pv = NULL;
73 IStream *pStream = NULL;
74 Bitmap *pBitmap = NULL;
75 Status gs = Ok;
76
77 + // Use ResReadData (RT_RCDATA) instead of Bitmap::FromResource (RT_BITMAP) to support all file formats that GDI+ supports.
78 hr = ResReadData(hinst, szId, &pvData, &cbData);
79 GdipExitOnFailure(hr, "Failed to load GDI+ bitmap from resource.");
80
@@ -159,6 +161,27 @@ LExit:
161 }
162
163
164 +/********************************************************************
165 + GdipBitmapToGdiBitmap - convert Gdiplus::Bitmap to HBITMAP.
166 +
167 +********************************************************************/
168 +extern "C" HRESULT DAPI GdipBitmapToGdiBitmap(
169 + __in Gdiplus::Bitmap* pBitmap,
170 + __out HBITMAP* phBitmap
171 + )
172 +{
173 + HRESULT hr = S_OK;
174 + Status gs = Ok;
175 + Color black;
176 +
177 + gs = pBitmap->GetHBITMAP(black, phBitmap);
178 + GdipExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP.");
179 +
180 +LExit:
181 + return hr;
182 +}
183 +
184 +
185 HRESULT DAPI GdipHresultFromStatus(
186 __in Gdiplus::Status gs
187 )
src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h
+5
@@ -30,6 +30,11 @@ HRESULT DAPI GdipBitmapFromFile(
30 __out Gdiplus::Bitmap **ppBitmap
31 );
32
33 +HRESULT DAPI GdipBitmapToGdiBitmap(
34 + __in Gdiplus::Bitmap* pBitmap,
35 + __out HBITMAP* phBitmap
36 + );
37 +
38 HRESULT DAPI GdipHresultFromStatus(
39 __in Gdiplus::Status gs
40 );
src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
+2 -1
@@ -167,6 +167,7 @@ struct THEME_CONTROL
167 LPWSTR sczVisibleCondition;
168 BOOL fDisableVariableFunctionality;
169
170 + Gdiplus::Bitmap* pBitmap;
171 HBITMAP hImage;
172 HICON hIcon;
173
@@ -296,7 +297,7 @@ struct THEME
297 int nSourceY;
298 UINT uStringId;
299
299 - HBITMAP hImage;
300 + Gdiplus::Bitmap* pBitmap;
301
302 DWORD cFonts;
303 THEME_FONT* rgFonts;
src/libs/dutil/WixToolset.DUtil/thmutil.cpp
+213 -94
@@ -91,7 +91,14 @@ static HRESULT GetAttributeImageFileOrResource(
91 __in_opt HMODULE hModule,
92 __in_z_opt LPCWSTR wzRelativePath,
93 __in IXMLDOMNode* pElement,
94 - __out HBITMAP* phImage
94 + __out Gdiplus::Bitmap** ppBitmap
95 + );
96 +static HRESULT ParseOwnerDrawImage(
97 + __in_opt HMODULE hModule,
98 + __in_z_opt LPCWSTR wzRelativePath,
99 + __in THEME* pTheme,
100 + __in IXMLDOMNode* pElement,
101 + __in THEME_CONTROL* pControl
102 );
103 static HRESULT ParseCommandLinkImage(
104 __in_opt HMODULE hModule,
@@ -274,11 +281,36 @@ static HRESULT DrawImage(
281 __in DRAWITEMSTRUCT* pdis,
282 __in const THEME_CONTROL* pControl
283 );
284 +static HRESULT DrawGdipBitmap(
285 + __in HDC hdc,
286 + __in int destX,
287 + __in int destY,
288 + __in int destWidth,
289 + __in int destHeight,
290 + __in Gdiplus::Bitmap* pBitmap,
291 + __in int srcX,
292 + __in int srcY,
293 + __in int srcWidth,
294 + __in int srcHeight
295 + );
296 static HRESULT DrawProgressBar(
297 __in THEME* pTheme,
298 __in DRAWITEMSTRUCT* pdis,
299 __in const THEME_CONTROL* pControl
300 );
301 +static HRESULT DrawProgressBarImage(
302 + __in THEME* pTheme,
303 + __in Gdiplus::Bitmap* pBitmap,
304 + __in int srcX,
305 + __in int srcY,
306 + __in int srcWidth,
307 + __in int srcHeight,
308 + __in HDC hdc,
309 + __in int destX,
310 + __in int destY,
311 + __in int destWidth,
312 + __in int destHeight
313 + );
314 static BOOL DrawHoverControl(
315 __in THEME* pTheme,
316 __in BOOL fHover
@@ -631,9 +663,9 @@ DAPI_(void) ThemeFree(
663 ReleaseMem(pTheme->rgPages);
664 ReleaseMem(pTheme->rgFonts);
665
634 - if (pTheme->hImage)
666 + if (pTheme->pBitmap)
667 {
636 - ::DeleteBitmap(pTheme->hImage);
668 + delete pTheme->pBitmap;
669 }
670
671 ReleaseStr(pTheme->sczCaption);
@@ -1273,19 +1305,9 @@ DAPI_(HRESULT) ThemeDrawBackground(
1305 {
1306 HRESULT hr = S_FALSE;
1307
1276 - if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase)
1308 + if (pTheme->pBitmap && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase)
1309 {
1278 - HDC hdcMem = ::CreateCompatibleDC(pps->hdc);
1279 - HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pTheme->hImage));
1280 - DWORD dwSourceWidth = pTheme->nDefaultDpiWidth;
1281 - DWORD dwSourceHeight = pTheme->nDefaultDpiHeight;
1282 -
1283 - ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY);
1284 -
1285 - ::SelectObject(hdcMem, hDefaultBitmap);
1286 - ::DeleteDC(hdcMem);
1287 -
1288 - hr = S_OK;
1310 + hr = DrawGdipBitmap(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, pTheme->pBitmap, pTheme->nSourceX, pTheme->nSourceY, pTheme->nDefaultDpiWidth, pTheme->nDefaultDpiHeight);
1311 }
1312
1313 return hr;
@@ -1733,7 +1755,7 @@ static HRESULT ParseTheme(
1755 pTheme->nDpi = USER_DEFAULT_SCREEN_DPI;
1756
1757 // Parse the optional background resource image.
1736 - hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pThemeElement, &pTheme->hImage);
1758 + hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pThemeElement, &pTheme->pBitmap);
1759 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed while parsing theme image.");
1760
1761 // Parse the fonts.
@@ -1762,7 +1784,7 @@ static HRESULT GetAttributeImageFileOrResource(
1784 __in_opt HMODULE hModule,
1785 __in_z_opt LPCWSTR wzRelativePath,
1786 __in IXMLDOMNode* pElement,
1765 - __out HBITMAP* phImage
1787 + __out Gdiplus::Bitmap** ppBitmap
1788 )
1789 {
1790 HRESULT hr = S_OK;
@@ -1771,7 +1793,7 @@ static HRESULT GetAttributeImageFileOrResource(
1793 WORD wResourceId = 0;
1794 BOOL fFound = FALSE;
1795 Gdiplus::Bitmap* pBitmap = NULL;
1774 - *phImage = NULL;
1796 + *ppBitmap = NULL;
1797
1798 hr = XmlGetAttributeUInt16(pElement, L"ImageResource", &wResourceId);
1799 ThmExitOnOptionalXmlQueryFailure(hr, fFound, "Failed to get image resource attribute.");
@@ -1808,12 +1830,10 @@ static HRESULT GetAttributeImageFileOrResource(
1830 ThmExitOnFailure(hr, "Failed to load image from file: %ls", sczImageFile);
1831 }
1832
1811 - // If there is an image, convert it into a bitmap handle.
1833 if (pBitmap)
1834 {
1814 - Gdiplus::Color black;
1815 - Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage);
1816 - ThmExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP.");
1835 + *ppBitmap = pBitmap;
1836 + pBitmap = NULL;
1837 }
1838 else
1839 {
@@ -1846,7 +1866,7 @@ static HRESULT ParseOwnerDrawImage(
1866 BOOL fFoundImage = FALSE;
1867
1868 // Parse the optional background resource image.
1849 - hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pControl->hImage);
1869 + hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pControl->pBitmap);
1870 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed while parsing control image.");
1871
1872 if (fXmlFound)
@@ -1854,7 +1874,7 @@ static HRESULT ParseOwnerDrawImage(
1874 fFoundImage = TRUE;
1875 }
1876
1857 - hr = ParseSourceXY(pElement, NULL != pTheme->hImage, &pControl->nSourceX, &pControl->nSourceY);
1877 + hr = ParseSourceXY(pElement, NULL != pTheme->pBitmap, &pControl->nSourceX, &pControl->nSourceY);
1878 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get control SourceX and SourceY attributes.");
1879
1880 if (fXmlFound)
@@ -1894,10 +1914,17 @@ static HRESULT ParseCommandLinkImage(
1914 BOOL fXmlFound = FALSE;
1915 LPWSTR sczIconFile = NULL;
1916 WORD wResourceId = 0;
1917 + Gdiplus::Bitmap* pBitmap = NULL;
1918
1898 - hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pControl->hImage);
1919 + hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pBitmap);
1920 ThmExitOnOptionalXmlQueryFailure(hr, fImageFound, "Failed to parse image attributes for CommandLink.");
1921
1922 + if (pBitmap)
1923 + {
1924 + hr = GdipBitmapToGdiBitmap(pBitmap, &pControl->hImage);
1925 + ThmExitOnFailure(hr, "Failed to convert bitmap for CommandLink.");
1926 + }
1927 +
1928 hr = XmlGetAttributeUInt16(pElement, L"IconResource", &wResourceId);
1929 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get icon resource attribute.");
1930
@@ -1945,6 +1972,11 @@ static HRESULT ParseCommandLinkImage(
1972 ThmExitOnUnexpectedAttribute(hr, pElement, L"CommandLink", L"SourceY");
1973
1974 LExit:
1975 + if (pBitmap)
1976 + {
1977 + delete pBitmap;
1978 + }
1979 +
1980 ReleaseStr(sczIconFile);
1981 ReleaseBSTR(bstr);
1982
@@ -2188,7 +2220,7 @@ static HRESULT ParseWindow(
2220 ReleaseNullBSTR(bstr);
2221 }
2222
2191 - hr = ParseSourceXY(pixn, NULL != pTheme->hImage, &pTheme->nSourceX, &pTheme->nSourceY);
2223 + hr = ParseSourceXY(pixn, NULL != pTheme->pBitmap, &pTheme->nSourceX, &pTheme->nSourceY);
2224 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window SourceX and SourceY attributes.");
2225
2226 // Parse the optional window style.
@@ -2510,8 +2542,8 @@ static HRESULT ParseImageLists(
2542 DWORD dwImageCount = 0;
2543 THEME_IMAGELIST* pThemeImageList = NULL;
2544 BOOL fXmlFound = FALSE;
2513 - HBITMAP hBitmap = NULL;
2514 - BITMAP bm = { };
2545 + Gdiplus::Bitmap* pBitmap = NULL;
2546 + HBITMAP hImage = NULL;
2547 DWORD i = 0;
2548 int iRetVal = 0;
2549
@@ -2551,13 +2583,18 @@ static HRESULT ParseImageLists(
2583
2584 while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL)))
2585 {
2554 - if (hBitmap)
2586 + if (pBitmap)
2587 + {
2588 + delete pBitmap;
2589 + pBitmap = NULL;
2590 + }
2591 + if (hImage)
2592 {
2556 - ::DeleteObject(hBitmap);
2557 - hBitmap = NULL;
2593 + ::DeleteObject(hImage);
2594 + hImage = NULL;
2595 }
2596
2560 - hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pixnImage, &hBitmap);
2597 + hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pixnImage, &pBitmap);
2598 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to parse image list: '%ls', item: %u", pThemeImageList->sczName, i);
2599
2600 if (!fXmlFound)
@@ -2567,13 +2604,14 @@ static HRESULT ParseImageLists(
2604
2605 if (0 == i)
2606 {
2570 - ::GetObjectW(hBitmap, sizeof(BITMAP), &bm);
2571 -
2572 - pThemeImageList->hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0);
2607 + pThemeImageList->hImageList = ImageList_Create(pBitmap->GetWidth(), pBitmap->GetHeight(), ILC_COLOR24, dwImageCount, 0);
2608 ThmExitOnNullWithLastError(pThemeImageList->hImageList, hr, "Failed to create image list.");
2609 }
2610
2576 - iRetVal = ImageList_Add(pThemeImageList->hImageList, hBitmap, NULL);
2611 + hr = GdipBitmapToGdiBitmap(pBitmap, &hImage);
2612 + ThmExitOnFailure(hr, "Failed to convert bitmap for CommandLink.");
2613 +
2614 + iRetVal = ImageList_Add(pThemeImageList->hImageList, hImage, NULL);
2615 if (-1 == iRetVal)
2616 {
2617 ThmExitWithLastError(hr, "Failed to add image %u to image list.", i);
@@ -2588,9 +2626,13 @@ static HRESULT ParseImageLists(
2626 }
2627
2628 LExit:
2591 - if (hBitmap)
2629 + if (hImage)
2630 {
2593 - ::DeleteObject(hBitmap);
2631 + ::DeleteObject(hImage);
2632 + }
2633 + if (pBitmap)
2634 + {
2635 + delete pBitmap;
2636 }
2637 ReleaseObject(pixnlImageLists);
2638 ReleaseObject(pixnImageList);
@@ -3846,39 +3888,37 @@ static HRESULT DrawButton(
3888 __in const THEME_CONTROL* pControl
3889 )
3890 {
3849 - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3850 - int nSourceY = pControl->hImage ? 0 : pControl->nSourceY;
3851 - DWORD dwSourceWidth = pControl->nDefaultDpiWidth;
3852 - DWORD dwSourceHeight = pControl->nDefaultDpiHeight;
3853 -
3854 - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3855 - HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
3891 + HRESULT hr = S_OK;
3892 + int nSourceX = pControl->pBitmap ? 0 : pControl->nSourceX;
3893 + int nSourceY = pControl->pBitmap ? 0 : pControl->nSourceY;
3894 + int nSourceWidth = pControl->pBitmap ? pControl->pBitmap->GetWidth() : pControl->nDefaultDpiWidth;
3895 + int nSourceHeight = pControl->pBitmap ? pControl->pBitmap->GetHeight() / 4 : pControl->nDefaultDpiHeight;
3896 + Gdiplus::Bitmap* pBitmap = pControl->pBitmap ? pControl->pBitmap : pTheme->pBitmap;
3897 + int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3898 + int nWidth = pdis->rcItem.right - pdis->rcItem.left;
3899
3900 DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE);
3901 // "clicked" gets priority
3902 if (ODS_SELECTED & pdis->itemState)
3903 {
3861 - nSourceY += pControl->nDefaultDpiHeight * 2;
3904 + nSourceY += nSourceHeight * 2;
3905 }
3906 // then hover
3907 else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
3908 {
3866 - nSourceY += pControl->nDefaultDpiHeight;
3909 + nSourceY += nSourceHeight;
3910 }
3911 // then focused
3869 - else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState)
3912 + else if ((WS_TABSTOP & dwStyle) && (ODS_FOCUS & pdis->itemState))
3913 {
3871 - nSourceY += pControl->nDefaultDpiHeight * 3;
3914 + nSourceY += nSourceHeight * 3;
3915 }
3916
3874 - ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY);
3875 -
3876 - ::SelectObject(hdcMem, hDefaultBitmap);
3877 - ::DeleteDC(hdcMem);
3917 + hr = DrawGdipBitmap(pdis->hDC, 0, 0, nWidth, nHeight, pBitmap, nSourceX, nSourceY, nSourceWidth, nSourceHeight);
3918
3919 DrawControlText(pTheme, pdis, pControl, TRUE, FALSE);
3920
3881 - return S_OK;
3921 + return hr;
3922 }
3923
3924
@@ -3953,31 +3993,69 @@ static HRESULT DrawImage(
3993 __in const THEME_CONTROL* pControl
3994 )
3995 {
3956 - DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3957 - DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left;
3958 - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3959 - int nSourceY = pControl->hImage ? 0 : pControl->nSourceY;
3960 - DWORD dwSourceHeight = pControl->nDefaultDpiHeight;
3961 - DWORD dwSourceWidth = pControl->nDefaultDpiWidth;
3996 + HRESULT hr = S_OK;
3997 + int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3998 + int nWidth = pdis->rcItem.right - pdis->rcItem.left;
3999 + int nSourceX = pControl->pBitmap ? 0 : pControl->nSourceX;
4000 + int nSourceY = pControl->pBitmap ? 0 : pControl->nSourceY;
4001 + int nSourceWidth = pControl->pBitmap ? pControl->pBitmap->GetWidth() : pControl->nDefaultDpiWidth;
4002 + int nSourceHeight = pControl->pBitmap ? pControl->pBitmap->GetHeight() : pControl->nDefaultDpiHeight;
4003 + Gdiplus::Bitmap* pBitmap = pControl->pBitmap ? pControl->pBitmap : pTheme->pBitmap;
4004 +
4005 + hr = DrawGdipBitmap(pdis->hDC, 0, 0, nWidth, nHeight, pBitmap, nSourceX, nSourceY, nSourceWidth, nSourceHeight);
4006 +
4007 + return hr;
4008 +}
4009
3963 - BLENDFUNCTION bf = { };
3964 - bf.BlendOp = AC_SRC_OVER;
3965 - bf.SourceConstantAlpha = 255;
3966 - bf.AlphaFormat = AC_SRC_ALPHA;
4010 +static HRESULT DrawGdipBitmap(
4011 + __in HDC hdc,
4012 + __in int destX,
4013 + __in int destY,
4014 + __in int destWidth,
4015 + __in int destHeight,
4016 + __in Gdiplus::Bitmap* pBitmap,
4017 + __in int srcX,
4018 + __in int srcY,
4019 + __in int srcWidth,
4020 + __in int srcHeight
4021 + )
4022 +{
4023 + // Note that this only indicates that GDI+ supports transparency from the source image type.
4024 + // Bitmaps with alpha information will return FALSE, while fully opaque PNGs will return TRUE.
4025 + BOOL fTransparency = (pBitmap->GetFlags() & Gdiplus::ImageFlagsHasAlpha) == Gdiplus::ImageFlagsHasAlpha;
4026 + Gdiplus::ImageAttributes attrs;
4027 + Gdiplus::Rect destRect(destX, destY, destWidth, destHeight);
4028 + Gdiplus::Graphics graphics(hdc);
4029 + Gdiplus::Status gs = Gdiplus::Status::Ok;
4030
3968 - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3969 - HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
4031 + // This fixes GDI+ behavior where it badly handles the edges of an image when scaling.
4032 + // This is easily seen when using an image Progressbar that's 4x1 - the progress bar fades to transparent in both directions.
4033 + attrs.SetWrapMode(Gdiplus::WrapMode::WrapModeTileFlipXY);
4034
3971 - // Try to draw the image with transparency and if that fails (usually because the image has no
3972 - // alpha channel) then draw the image as is.
3973 - if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, bf))
4035 + // graphics.SmoothingMode is not set because it has no impact on DrawImage.
4036 +
4037 + // This is the best interpolation mode, and the only one that is decent at downscaling.
4038 + graphics.SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQualityBicubic);
4039 +
4040 + // There's a significant quality improvement when scaling to use the HighQuality pixel offset mode with no measurable difference in performance.
4041 + graphics.SetPixelOffsetMode(Gdiplus::PixelOffsetMode::PixelOffsetModeHighQuality);
4042 +
4043 + // If there's transparency, make sure that the blending is done with high quality.
4044 + // If not, try to skip blending.
4045 + if (fTransparency)
4046 {
3975 - ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY);
4047 + graphics.SetCompositingMode(Gdiplus::CompositingMode::CompositingModeSourceOver);
4048 + graphics.SetCompositingQuality(Gdiplus::CompositingQuality::CompositingQualityHighQuality);
4049 + }
4050 + else
4051 + {
4052 + graphics.SetCompositingMode(Gdiplus::CompositingMode::CompositingModeSourceCopy);
4053 + graphics.SetCompositingQuality(Gdiplus::CompositingQuality::CompositingQualityHighSpeed);
4054 }
4055
3978 - ::SelectObject(hdcMem, hDefaultBitmap);
3979 - ::DeleteDC(hdcMem);
3980 - return S_OK;
4056 + gs = graphics.DrawImage(pBitmap, destRect, srcX, srcY, srcWidth, srcHeight, Gdiplus::Unit::UnitPixel, &attrs);
4057 +
4058 + return GdipHresultFromStatus(gs);
4059 }
4060
4061
@@ -3987,38 +4065,74 @@ static HRESULT DrawProgressBar(
4065 __in const THEME_CONTROL* pControl
4066 )
4067 {
3990 - DWORD dwProgressColor = HIWORD(pControl->dwData);
3991 - DWORD dwProgressPercentage = LOWORD(pControl->dwData);
3992 - DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3993 - DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100;
3994 - DWORD dwSourceHeight = pControl->nDefaultDpiHeight;
3995 - int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3996 - int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * dwSourceHeight);
4068 + const int nSideWidth = 1;
4069 + HRESULT hr = S_OK;
4070 + WORD wProgressColor = HIWORD(pControl->dwData);
4071 + WORD wProgressPercentage = LOWORD(pControl->dwData);
4072 + int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
4073 + int nSourceHeight = pControl->nDefaultDpiHeight;
4074 + int nSourceX = pControl->pBitmap ? 0 : pControl->nSourceX;
4075 + int nSourceY = (pControl->pBitmap ? 0 : pControl->nSourceY) + (wProgressColor * nSourceHeight);
4076 + int nFillableWidth = pdis->rcItem.right - 2 * nSideWidth;
4077 + int nCenter = nFillableWidth > 0 ? nFillableWidth * wProgressPercentage / 100 : 0;
4078 + Gdiplus::Bitmap* pBitmap = pControl->pBitmap ? pControl->pBitmap : pTheme->pBitmap;
4079
3998 - HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3999 - HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
4080 + if (0 > nFillableWidth)
4081 + {
4082 + ExitFunction1(hr = S_FALSE);
4083 + }
4084
4085 // Draw the left side of the progress bar.
4002 - ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY);
4086 + hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX, nSourceY, 1, nSourceHeight, pdis->hDC, 0, 0, nSideWidth, nHeight);
4087
4088 // Draw the filled side of the progress bar, if there is any.
4005 - if (0 < dwCenter)
4089 + if (0 < nCenter)
4090 {
4007 - ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwSourceHeight, SRCCOPY);
4091 + hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX + 1, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth, 0, nCenter, nHeight);
4092 }
4093
4094 // Draw the unfilled side of the progress bar, if there is any.
4011 - if (dwCenter < static_cast<DWORD>(pdis->rcItem.right - 2))
4095 + if (nCenter < nFillableWidth)
4096 {
4013 - ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwSourceHeight, SRCCOPY);
4097 + hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX + 2, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth + nCenter, 0, pdis->rcItem.right - nCenter - nSideWidth, nHeight);
4098 }
4099
4100 // Draw the right side of the progress bar.
4017 - ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX + 3, nSourceY, 1, dwSourceHeight, SRCCOPY);
4101 + hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX + 3, nSourceY, 1, nSourceHeight, pdis->hDC, pdis->rcItem.right - nSideWidth, 0, nSideWidth, nHeight);
4102
4019 - ::SelectObject(hdcMem, hDefaultBitmap);
4020 - ::DeleteDC(hdcMem);
4021 - return S_OK;
4103 +LExit:
4104 + return hr;
4105 +}
4106 +
4107 +static HRESULT DrawProgressBarImage(
4108 + __in THEME* /*pTheme*/,
4109 + __in Gdiplus::Bitmap* pBitmap,
4110 + __in int srcX,
4111 + __in int srcY,
4112 + __in int srcWidth,
4113 + __in int srcHeight,
4114 + __in HDC hdc,
4115 + __in int destX,
4116 + __in int destY,
4117 + __in int destWidth,
4118 + __in int destHeight
4119 + )
4120 +{
4121 + HRESULT hr = S_OK;
4122 + Gdiplus::Rect dest(0, 0, srcWidth, srcHeight);
4123 + Gdiplus::Bitmap isolated(dest.Width, dest.Height);
4124 + Gdiplus::Graphics graphics(&isolated);
4125 + graphics.SetCompositingMode(Gdiplus::CompositingMode::CompositingModeSourceCopy);
4126 +
4127 + // Isolate the source rectangle into a temporary bitmap because otherwise GDI+ would use pixels outside of that rectangle when stretching.
4128 + Gdiplus::Status gs = graphics.DrawImage(pBitmap, dest, srcX, srcY, srcWidth, srcHeight, Gdiplus::Unit::UnitPixel);
4129 + hr = GdipHresultFromStatus(gs);
4130 + if (SUCCEEDED(hr))
4131 + {
4132 + hr = DrawGdipBitmap(hdc, destX, destY, destWidth, destHeight, &isolated, 0, 0, isolated.GetWidth(), isolated.GetHeight());
4133 + }
4134 +
4135 + return hr;
4136 }
4137
4138
@@ -4104,6 +4218,11 @@ static void FreeControl(
4218 ReleaseStr(pControl->sczValue);
4219 ReleaseStr(pControl->sczVariable);
4220
4221 + if (pControl->pBitmap)
4222 + {
4223 + delete pControl->pBitmap;
4224 + }
4225 +
4226 if (pControl->hImage)
4227 {
4228 ::DeleteBitmap(pControl->hImage);
@@ -5203,7 +5322,7 @@ static HRESULT LoadControls(
5322 __fallthrough;
5323 case THEME_CONTROL_TYPE_BUTTON:
5324 wzWindowClass = WC_BUTTONW;
5206 - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5325 + if (pControl->pBitmap || (pTheme->pBitmap && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5326 {
5327 dwWindowBits |= BS_OWNERDRAW;
5328 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
@@ -5237,7 +5356,7 @@ static HRESULT LoadControls(
5356 break;
5357
5358 case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps).
5240 - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5359 + if (pControl->pBitmap || (pTheme->pBitmap && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5360 {
5361 wzWindowClass = THEME_WC_STATICOWNERDRAW;
5362 dwWindowBits |= SS_OWNERDRAW;
@@ -5264,7 +5383,7 @@ static HRESULT LoadControls(
5383 break;
5384
5385 case THEME_CONTROL_TYPE_PROGRESSBAR:
5267 - if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5386 + if (pControl->pBitmap || (pTheme->pBitmap && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5387 {
5388 wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control.
5389 dwWindowBits |= SS_OWNERDRAW;