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;