From b22a62fd6eed5bb7a2c04d51828438daa621db6c Mon Sep 17 00:00:00 2001
From: Sean Hall <r.sean.hall@gmail.com>
Date: Fri, 4 Jun 2021 13:15:28 -0500
Subject: Add THEME_IMAGE_REFERENCE to thmutil.

---
 src/libs/dutil/WixToolset.DUtil/inc/thmutil.h |  35 ++-
 src/libs/dutil/WixToolset.DUtil/thmutil.cpp   | 328 +++++++++++++++++++++-----
 2 files changed, 293 insertions(+), 70 deletions(-)

diff --git a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
index 32382793..2a7cabb9 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
@@ -75,6 +75,13 @@ typedef enum THEME_CONTROL_TYPE
     THEME_CONTROL_TYPE_TAB,
 } THEME_CONTROL_TYPE;
 
+typedef enum THEME_IMAGE_REFERENCE_TYPE
+{
+    THEME_IMAGE_REFERENCE_TYPE_NONE,
+    THEME_IMAGE_REFERENCE_TYPE_PARTIAL,
+    THEME_IMAGE_REFERENCE_TYPE_COMPLETE,
+} THEME_IMAGE_REFERENCE_TYPE;
+
 typedef enum THEME_SHOW_PAGE_REASON
 {
     THEME_SHOW_PAGE_REASON_DEFAULT,
@@ -100,6 +107,22 @@ struct THEME_COLUMN
 };
 
 
+struct THEME_IMAGE_REFERENCE
+{
+    THEME_IMAGE_REFERENCE_TYPE type;
+    DWORD dwImageInstanceIndex;
+    int nX;
+    int nY;
+    int nHeight;
+    int nWidth;
+};
+
+struct THEME_IMAGE_INSTANCE
+{
+    Gdiplus::Bitmap* pBitmap;
+};
+
+
 struct THEME_TAB
 {
     LPWSTR pszName;
@@ -159,15 +182,13 @@ struct THEME_CONTROL
     int nY;
     int nHeight;
     int nWidth;
-    int nSourceX;
-    int nSourceY;
     UINT uStringId;
 
     LPWSTR sczEnableCondition;
     LPWSTR sczVisibleCondition;
     BOOL fDisableVariableFunctionality;
 
-    Gdiplus::Bitmap* pBitmap;
+    THEME_IMAGE_REFERENCE imageRef;
     HBITMAP hImage;
     HICON hIcon;
 
@@ -293,15 +314,17 @@ struct THEME
     int nMinimumWidth;
     int nWindowHeight;
     int nWindowWidth;
-    int nSourceX;
-    int nSourceY;
     UINT uStringId;
 
-    Gdiplus::Bitmap* pBitmap;
+    DWORD dwSourceImageInstanceIndex;
+    THEME_IMAGE_REFERENCE windowImageRef;
 
     DWORD cFonts;
     THEME_FONT* rgFonts;
 
+    DWORD cStandaloneImages;
+    THEME_IMAGE_INSTANCE* rgStandaloneImages;
+
     DWORD cPages;
     THEME_PAGE* rgPages;
 
diff --git a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
index 21460ad1..363c3be1 100644
--- a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
@@ -42,6 +42,7 @@
 const DWORD THEME_INVALID_ID = 0xFFFFFFFF;
 const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF;
 const DWORD GROW_FONT_INSTANCES = 3;
+const DWORD GROW_IMAGE_INSTANCES = 5;
 const DWORD GROW_WINDOW_TEXT = 250;
 const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink";
 const LPCWSTR THEME_WC_PANEL = L"ThemePanel";
@@ -87,6 +88,11 @@ static HRESULT ParseTheme(
     __in IXMLDOMDocument* pixd,
     __out THEME** ppTheme
     );
+static HRESULT AddStandaloneImage(
+    __in THEME* pTheme,
+    __in Gdiplus::Bitmap** ppBitmap,
+    __out DWORD* pdwIndex
+    );
 static HRESULT GetAttributeImageFileOrResource(
     __in_opt HMODULE hModule,
     __in_z_opt LPCWSTR wzRelativePath,
@@ -118,9 +124,10 @@ static HRESULT GetAttributeFontId(
     );
 static HRESULT ParseSourceXY(
     __in IXMLDOMNode* pixn,
-    __in BOOL fAllowed,
-    __inout int* pnX,
-    __inout int* pnY
+    __in THEME* pTheme,
+    __in int nWidth,
+    __in int nHeight,
+    __inout THEME_IMAGE_REFERENCE* pReference
     );
 static HRESULT ParseWindow(
     __in_opt HMODULE hModule,
@@ -281,6 +288,20 @@ static HRESULT DrawImage(
     __in DRAWITEMSTRUCT* pdis,
     __in const THEME_CONTROL* pControl
     );
+static void GetImageInstance(
+    __in THEME* pTheme,
+    __in const THEME_IMAGE_REFERENCE* pReference,
+    __out const THEME_IMAGE_INSTANCE** ppInstance
+    );
+static HRESULT DrawImageReference(
+    __in THEME* pTheme,
+    __in const THEME_IMAGE_REFERENCE* pReference,
+    __in HDC hdc,
+    __in int destX,
+    __in int destY,
+    __in int destWidth,
+    __in int destHeight
+    );
 static HRESULT DrawGdipBitmap(
     __in HDC hdc,
     __in int destX,
@@ -300,7 +321,7 @@ static HRESULT DrawProgressBar(
     );
 static HRESULT DrawProgressBarImage(
     __in THEME* pTheme,
-    __in Gdiplus::Bitmap* pBitmap,
+    __in const THEME_IMAGE_INSTANCE* pImageInstance,
     __in int srcX,
     __in int srcY,
     __in int srcWidth,
@@ -333,6 +354,9 @@ static void FreeFontInstance(
 static void FreeFont(
     __in THEME_FONT* pFont
     );
+static void FreeImageInstance(
+    __in THEME_IMAGE_INSTANCE* pImageInstance
+    );
 static void FreePage(
     __in THEME_PAGE* pPage
     );
@@ -644,6 +668,11 @@ DAPI_(void) ThemeFree(
             FreeFont(pTheme->rgFonts + i);
         }
 
+        for (DWORD i = 0; i < pTheme->cStandaloneImages; ++i)
+        {
+            FreeImageInstance(pTheme->rgStandaloneImages + i);
+        }
+
         for (DWORD i = 0; i < pTheme->cPages; ++i)
         {
             FreePage(pTheme->rgPages + i);
@@ -661,13 +690,9 @@ DAPI_(void) ThemeFree(
 
         ReleaseMem(pTheme->rgControls);
         ReleaseMem(pTheme->rgPages);
+        ReleaseMem(pTheme->rgStandaloneImages);
         ReleaseMem(pTheme->rgFonts);
 
-        if (pTheme->pBitmap)
-        {
-            delete pTheme->pBitmap;
-        }
-
         ReleaseStr(pTheme->sczCaption);
         ReleaseMem(pTheme);
     }
@@ -1305,9 +1330,9 @@ DAPI_(HRESULT) ThemeDrawBackground(
 {
     HRESULT hr = S_FALSE;
 
-    if (pTheme->pBitmap && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase)
+    if (pps->fErase && THEME_IMAGE_REFERENCE_TYPE_NONE != pTheme->windowImageRef.type)
     {
-        hr = DrawGdipBitmap(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, pTheme->pBitmap, pTheme->nSourceX, pTheme->nSourceY, pTheme->nDefaultDpiWidth, pTheme->nDefaultDpiHeight);
+        hr = DrawImageReference(pTheme, &pTheme->windowImageRef, pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight);
     }
 
     return hr;
@@ -1743,6 +1768,7 @@ static HRESULT ParseTheme(
     HRESULT hr = S_OK;
     THEME* pTheme = NULL;
     IXMLDOMElement *pThemeElement = NULL;
+    Gdiplus::Bitmap* pBitmap = NULL;
     BOOL fXmlFound = FALSE;
 
     hr = pixd->get_documentElement(&pThemeElement);
@@ -1755,9 +1781,19 @@ static HRESULT ParseTheme(
     pTheme->nDpi = USER_DEFAULT_SCREEN_DPI;
 
     // Parse the optional background resource image.
-    hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pThemeElement, &pTheme->pBitmap);
+    hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pThemeElement, &pBitmap);
     ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed while parsing theme image.");
 
+    if (fXmlFound)
+    {
+        hr = AddStandaloneImage(pTheme, &pBitmap, &pTheme->dwSourceImageInstanceIndex);
+        ThmExitOnFailure(hr, "Failed to store theme image.");
+    }
+    else
+    {
+        pTheme->dwSourceImageInstanceIndex = THEME_INVALID_ID;
+    }
+
     // Parse the fonts.
     hr = ParseFonts(pThemeElement, pTheme);
     ThmExitOnFailure(hr, "Failed to parse theme fonts.");
@@ -1772,6 +1808,11 @@ static HRESULT ParseTheme(
 LExit:
     ReleaseObject(pThemeElement);
 
+    if (pBitmap)
+    {
+        delete pBitmap;
+    }
+
     if (pTheme)
     {
         ThemeFree(pTheme);
@@ -1780,6 +1821,30 @@ LExit:
     return hr;
 }
 
+static HRESULT AddStandaloneImage(
+    __in THEME* pTheme,
+    __in Gdiplus::Bitmap** ppBitmap,
+    __out DWORD* pdwIndex
+    )
+{
+    HRESULT hr = S_OK;
+    THEME_IMAGE_INSTANCE* pInstance = NULL;
+
+    hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pTheme->rgStandaloneImages), pTheme->cStandaloneImages, 1, sizeof(THEME_IMAGE_INSTANCE), GROW_IMAGE_INSTANCES);
+    ThmExitOnFailure(hr, "Failed to allocate memory for image instances.");
+
+    *pdwIndex = pTheme->cStandaloneImages;
+    ++pTheme->cStandaloneImages;
+
+    pInstance = pTheme->rgStandaloneImages + *pdwIndex;
+
+    pInstance->pBitmap = *ppBitmap;
+    *ppBitmap = NULL;
+
+LExit:
+    return hr;
+}
+
 static HRESULT GetAttributeImageFileOrResource(
     __in_opt HMODULE hModule,
     __in_z_opt LPCWSTR wzRelativePath,
@@ -1864,17 +1929,23 @@ static HRESULT ParseOwnerDrawImage(
     HRESULT hr = S_OK;
     BOOL fXmlFound = FALSE;
     BOOL fFoundImage = FALSE;
+    Gdiplus::Bitmap* pBitmap = NULL;
 
     // Parse the optional background resource image.
-    hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pControl->pBitmap);
+    hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pBitmap);
     ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed while parsing control image.");
 
     if (fXmlFound)
     {
+        hr = AddStandaloneImage(pTheme, &pBitmap, &pControl->imageRef.dwImageInstanceIndex);
+        ThmExitOnFailure(hr, "Failed to store owner draw image.");
+
+        pControl->imageRef.type = THEME_IMAGE_REFERENCE_TYPE_COMPLETE;
+
         fFoundImage = TRUE;
     }
 
-    hr = ParseSourceXY(pElement, NULL != pTheme->pBitmap, &pControl->nSourceX, &pControl->nSourceY);
+    hr = ParseSourceXY(pElement, pTheme, pControl->nWidth, pControl->nHeight, &pControl->imageRef);
     ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get control SourceX and SourceY attributes.");
 
     if (fXmlFound)
@@ -1897,6 +1968,11 @@ static HRESULT ParseOwnerDrawImage(
     }
 
 LExit:
+    if (pBitmap)
+    {
+        delete pBitmap;
+    }
+
     return hr;
 }
 
@@ -2042,35 +2118,50 @@ LExit:
 
 static HRESULT ParseSourceXY(
     __in IXMLDOMNode* pixn,
-    __in BOOL fAllowed,
-    __inout int* pnX,
-    __inout int* pnY
+    __in THEME* pTheme,
+    __in int nWidth,
+    __in int nHeight,
+    __inout THEME_IMAGE_REFERENCE* pReference
     )
 {
     HRESULT hr = S_OK;
     BOOL fXFound = FALSE;
     BOOL fYFound = FALSE;
-
-    hr = GetAttributeCoordinateOrDimension(pixn, L"SourceX", pnX);
+    int nX = 0;
+    int nY = 0;
+    DWORD dwImageInstanceIndex = pTheme->dwSourceImageInstanceIndex;
+    THEME_IMAGE_INSTANCE* pInstance = THEME_INVALID_ID != dwImageInstanceIndex ? pTheme->rgStandaloneImages + dwImageInstanceIndex : NULL;
+    int nSourceWidth = pInstance ? pInstance->pBitmap->GetWidth() : 0;
+    int nSourceHeight = pInstance ? pInstance->pBitmap->GetHeight() : 0;
+
+    hr = GetAttributeCoordinateOrDimension(pixn, L"SourceX", &nX);
     ThmExitOnOptionalXmlQueryFailure(hr, fXFound, "Failed to get SourceX attribute.");
 
     if (!fXFound)
     {
-        *pnX = -1;
+        nX = -1;
     }
     else
     {
-        if (!fAllowed)
+        if (!pInstance)
         {
             ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX cannot be specified without an image specified on Theme.");
         }
-        else if (0 > *pnX)
+        else if (0 > nX)
         {
             ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX must be non-negative.");
         }
+        else if (nSourceWidth <= nX)
+        {
+            ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX (%i) must be less than the image width: %i.", nX, nSourceWidth);
+        }
+        else if (nSourceWidth <= (nX + nWidth))
+        {
+            ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX (%i) with width %i must be less than the image width: %i.", nX, nWidth, nSourceWidth);
+        }
     }
 
-    hr = GetAttributeCoordinateOrDimension(pixn, L"SourceY", pnY);
+    hr = GetAttributeCoordinateOrDimension(pixn, L"SourceY", &nY);
     ThmExitOnOptionalXmlQueryFailure(hr, fYFound, "Failed to get SourceY attribute.");
 
     if (!fYFound)
@@ -2080,12 +2171,11 @@ static HRESULT ParseSourceXY(
             ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY must be specified with SourceX.");
         }
 
-        *pnY = -1;
-        hr = E_NOTFOUND;
+        ExitFunction1(hr = E_NOTFOUND);
     }
     else
     {
-        if (!fAllowed)
+        if (!pInstance)
         {
             ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY cannot be specified without an image specified on Theme.");
         }
@@ -2093,12 +2183,27 @@ static HRESULT ParseSourceXY(
         {
             ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY must be specified with SourceX.");
         }
-        else if (0 > *pnY)
+        else if (0 > nY)
         {
             ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY must be non-negative.");
         }
+        else if (nSourceHeight <= nY)
+        {
+            ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY (%i) must be less than the image height: %i.", nY, nSourceHeight);
+        }
+        else if (nSourceHeight <= (nY + nHeight))
+        {
+            ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY (%i) with height %i must be less than the image height: %i.", nY, nHeight, nSourceHeight);
+        }
     }
 
+    pReference->type = THEME_IMAGE_REFERENCE_TYPE_PARTIAL;
+    pReference->dwImageInstanceIndex = dwImageInstanceIndex;
+    pReference->nX = nX;
+    pReference->nY = nY;
+    pReference->nWidth = nWidth;
+    pReference->nHeight = nHeight;
+
 LExit:
     return hr;
 }
@@ -2220,7 +2325,7 @@ static HRESULT ParseWindow(
         ReleaseNullBSTR(bstr);
     }
 
-    hr = ParseSourceXY(pixn, NULL != pTheme->pBitmap, &pTheme->nSourceX, &pTheme->nSourceY);
+    hr = ParseSourceXY(pixn, pTheme, pTheme->nDefaultDpiWidth, pTheme->nDefaultDpiHeight, &pTheme->windowImageRef);
     ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window SourceX and SourceY attributes.");
 
     // Parse the optional window style.
@@ -2230,7 +2335,7 @@ static HRESULT ParseWindow(
     if (!fXmlFound)
     {
         pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION;
-        pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED;
+        pTheme->dwStyle |= (THEME_IMAGE_REFERENCE_TYPE_NONE != pTheme->windowImageRef.type) ? WS_POPUP : WS_OVERLAPPED;
     }
 
     hr = XmlGetAttributeUInt32(pixn, L"StringId", reinterpret_cast<DWORD*>(&pTheme->uStringId));
@@ -3171,8 +3276,6 @@ static void InitializeThemeControl(
     pControl->dwFontHoverId = THEME_INVALID_ID;
     pControl->dwFontId = THEME_INVALID_ID;
     pControl->dwFontSelectedId = THEME_INVALID_ID;
-    pControl->nSourceX = -1;
-    pControl->nSourceY = -1;
     pControl->uStringId = UINT_MAX;
 }
 
@@ -3889,35 +3992,57 @@ static HRESULT DrawButton(
     )
 {
     HRESULT hr = S_OK;
-    int nSourceX = pControl->pBitmap ? 0 : pControl->nSourceX;
-    int nSourceY = pControl->pBitmap ? 0 : pControl->nSourceY;
-    int nSourceWidth = pControl->pBitmap ? pControl->pBitmap->GetWidth() : pControl->nDefaultDpiWidth;
-    int nSourceHeight = pControl->pBitmap ? pControl->pBitmap->GetHeight() / 4 : pControl->nDefaultDpiHeight;
-    Gdiplus::Bitmap* pBitmap = pControl->pBitmap ? pControl->pBitmap : pTheme->pBitmap;
+    THEME_IMAGE_REFERENCE buttonImageRef = { };
+    const THEME_IMAGE_INSTANCE* pInstance = NULL;
     int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
     int nWidth = pdis->rcItem.right - pdis->rcItem.left;
 
+    buttonImageRef.type = THEME_IMAGE_REFERENCE_TYPE_PARTIAL;
+    buttonImageRef.dwImageInstanceIndex = pControl->imageRef.dwImageInstanceIndex;
+    GetImageInstance(pTheme, &pControl->imageRef, &pInstance);
+
+    if (THEME_IMAGE_REFERENCE_TYPE_PARTIAL == pControl->imageRef.type)
+    {
+        buttonImageRef.nX = pControl->imageRef.nX;
+        buttonImageRef.nY = pControl->imageRef.nY;
+        buttonImageRef.nWidth = pControl->imageRef.nWidth;
+        buttonImageRef.nHeight = pControl->imageRef.nHeight;
+    }
+    else if (THEME_IMAGE_REFERENCE_TYPE_COMPLETE == pControl->imageRef.type)
+    {
+        buttonImageRef.nX = 0;
+        buttonImageRef.nY = 0;
+        buttonImageRef.nWidth = pInstance->pBitmap->GetWidth();
+        buttonImageRef.nHeight = pInstance->pBitmap->GetHeight() / 4;
+    }
+    else
+    {
+        AssertSz(FALSE, "Invalid image reference type for drawing");
+        ExitFunction1(hr = E_INVALIDARG);
+    }
+
     DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE);
     // "clicked" gets priority
     if (ODS_SELECTED & pdis->itemState)
     {
-        nSourceY += nSourceHeight * 2;
+        buttonImageRef.nY += buttonImageRef.nHeight * 2;
     }
     // then hover
     else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
     {
-        nSourceY += nSourceHeight;
+        buttonImageRef.nY += buttonImageRef.nHeight;
     }
     // then focused
     else if ((WS_TABSTOP & dwStyle) && (ODS_FOCUS & pdis->itemState))
     {
-        nSourceY += nSourceHeight * 3;
+        buttonImageRef.nY += buttonImageRef.nHeight * 3;
     }
 
-    hr = DrawGdipBitmap(pdis->hDC, 0, 0, nWidth, nHeight, pBitmap, nSourceX, nSourceY, nSourceWidth, nSourceHeight);
+    hr = DrawImageReference(pTheme, &buttonImageRef, pdis->hDC, 0, 0, nWidth, nHeight);
 
     DrawControlText(pTheme, pdis, pControl, TRUE, FALSE);
 
+LExit:
     return hr;
 }
 
@@ -3996,17 +4121,65 @@ static HRESULT DrawImage(
     HRESULT hr = S_OK;
     int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
     int nWidth = pdis->rcItem.right - pdis->rcItem.left;
-    int nSourceX = pControl->pBitmap ? 0 : pControl->nSourceX;
-    int nSourceY = pControl->pBitmap ? 0 : pControl->nSourceY;
-    int nSourceWidth = pControl->pBitmap ? pControl->pBitmap->GetWidth() : pControl->nDefaultDpiWidth;
-    int nSourceHeight = pControl->pBitmap ? pControl->pBitmap->GetHeight() : pControl->nDefaultDpiHeight;
-    Gdiplus::Bitmap* pBitmap = pControl->pBitmap ? pControl->pBitmap : pTheme->pBitmap;
 
-    hr = DrawGdipBitmap(pdis->hDC, 0, 0, nWidth, nHeight, pBitmap, nSourceX, nSourceY, nSourceWidth, nSourceHeight);
+    hr = DrawImageReference(pTheme, &pControl->imageRef, pdis->hDC, 0, 0, nWidth, nHeight);
 
     return hr;
 }
 
+static void GetImageInstance(
+    __in THEME* pTheme,
+    __in const THEME_IMAGE_REFERENCE* pReference,
+    __out const THEME_IMAGE_INSTANCE** ppInstance
+    )
+{
+    *ppInstance = pTheme->rgStandaloneImages + pReference->dwImageInstanceIndex;
+}
+
+static HRESULT DrawImageReference(
+    __in THEME* pTheme,
+    __in const THEME_IMAGE_REFERENCE* pReference,
+    __in HDC hdc,
+    __in int destX,
+    __in int destY,
+    __in int destWidth,
+    __in int destHeight
+    )
+{
+    HRESULT hr = S_OK;
+    const THEME_IMAGE_INSTANCE* pImageInstance = NULL;
+    int nX = 0;
+    int nY = 0;
+    int nWidth = 0;
+    int nHeight = 0;
+
+    GetImageInstance(pTheme, pReference, &pImageInstance);
+    if (THEME_IMAGE_REFERENCE_TYPE_PARTIAL == pReference->type)
+    {
+        nX = pReference->nX;
+        nY = pReference->nY;
+        nWidth = pReference->nWidth;
+        nHeight = pReference->nHeight;
+    }
+    else if (THEME_IMAGE_REFERENCE_TYPE_COMPLETE == pReference->type)
+    {
+        nX = 0;
+        nY = 0;
+        nWidth = pImageInstance->pBitmap->GetWidth();
+        nHeight = pImageInstance->pBitmap->GetHeight();
+    }
+    else
+    {
+        AssertSz(FALSE, "Invalid image reference type for drawing");
+        ExitFunction1(hr = E_INVALIDARG);
+    }
+
+    hr = DrawGdipBitmap(hdc, destX, destY, destWidth, destHeight, pImageInstance->pBitmap, nX, nY, nWidth, nHeight);
+
+LExit:
+    return hr;
+}
+
 static HRESULT DrawGdipBitmap(
     __in HDC hdc,
     __in int destX,
@@ -4069,36 +4242,56 @@ static HRESULT DrawProgressBar(
     HRESULT hr = S_OK;
     WORD wProgressColor = HIWORD(pControl->dwData);
     WORD wProgressPercentage = LOWORD(pControl->dwData);
+    const THEME_IMAGE_INSTANCE* pInstance = NULL;
     int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
-    int nSourceHeight = pControl->nDefaultDpiHeight;
-    int nSourceX = pControl->pBitmap ? 0 : pControl->nSourceX;
-    int nSourceY = (pControl->pBitmap ? 0 : pControl->nSourceY) + (wProgressColor * nSourceHeight);
+    int nSourceHeight = 0;
+    int nSourceX = 0;
+    int nSourceY = 0;
     int nFillableWidth = pdis->rcItem.right - 2 * nSideWidth;
     int nCenter = nFillableWidth > 0 ? nFillableWidth * wProgressPercentage / 100 : 0;
-    Gdiplus::Bitmap* pBitmap = pControl->pBitmap ? pControl->pBitmap : pTheme->pBitmap;
 
     if (0 > nFillableWidth)
     {
         ExitFunction1(hr = S_FALSE);
     }
 
+    GetImageInstance(pTheme, &pControl->imageRef, &pInstance);
+
+    if (THEME_IMAGE_REFERENCE_TYPE_PARTIAL == pControl->imageRef.type)
+    {
+        nSourceHeight = pControl->imageRef.nHeight;
+        nSourceX = pControl->imageRef.nX;
+        nSourceY = pControl->imageRef.nY + (wProgressColor * nSourceHeight);
+    }
+    else if (THEME_IMAGE_REFERENCE_TYPE_COMPLETE == pControl->imageRef.type)
+    {
+        nSourceHeight = pControl->nDefaultDpiHeight;
+        nSourceX = 0;
+        nSourceY = wProgressColor * nSourceHeight;
+    }
+    else
+    {
+        AssertSz(FALSE, "Invalid image reference type for drawing");
+        ExitFunction1(hr = E_INVALIDARG);
+    }
+
     // Draw the left side of the progress bar.
-    hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX, nSourceY, 1, nSourceHeight, pdis->hDC, 0, 0, nSideWidth, nHeight);
+    hr = DrawProgressBarImage(pTheme, pInstance, nSourceX, nSourceY, 1, nSourceHeight, pdis->hDC, 0, 0, nSideWidth, nHeight);
 
     // Draw the filled side of the progress bar, if there is any.
     if (0 < nCenter)
     {
-        hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX + 1, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth, 0, nCenter, nHeight);
+        hr = DrawProgressBarImage(pTheme, pInstance, nSourceX + 1, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth, 0, nCenter, nHeight);
     }
 
     // Draw the unfilled side of the progress bar, if there is any.
     if (nCenter < nFillableWidth)
     {
-        hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX + 2, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth + nCenter, 0, pdis->rcItem.right - nCenter - nSideWidth, nHeight);
+        hr = DrawProgressBarImage(pTheme, pInstance, nSourceX + 2, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth + nCenter, 0, pdis->rcItem.right - nCenter - nSideWidth, nHeight);
     }
 
     // Draw the right side of the progress bar.
-    hr = DrawProgressBarImage(pTheme, pBitmap, nSourceX + 3, nSourceY, 1, nSourceHeight, pdis->hDC, pdis->rcItem.right - nSideWidth, 0, nSideWidth, nHeight);
+    hr = DrawProgressBarImage(pTheme, pInstance, nSourceX + 3, nSourceY, 1, nSourceHeight, pdis->hDC, pdis->rcItem.right - nSideWidth, 0, nSideWidth, nHeight);
 
 LExit:
     return hr;
@@ -4106,7 +4299,7 @@ LExit:
 
 static HRESULT DrawProgressBarImage(
     __in THEME* /*pTheme*/,
-    __in Gdiplus::Bitmap* pBitmap,
+    __in const THEME_IMAGE_INSTANCE* pImageInstance,
     __in int srcX,
     __in int srcY,
     __in int srcWidth,
@@ -4125,7 +4318,7 @@ static HRESULT DrawProgressBarImage(
     graphics.SetCompositingMode(Gdiplus::CompositingMode::CompositingModeSourceCopy);
 
     // Isolate the source rectangle into a temporary bitmap because otherwise GDI+ would use pixels outside of that rectangle when stretching.
-    Gdiplus::Status gs = graphics.DrawImage(pBitmap, dest, srcX, srcY, srcWidth, srcHeight, Gdiplus::Unit::UnitPixel);
+    Gdiplus::Status gs = graphics.DrawImage(pImageInstance->pBitmap, dest, srcX, srcY, srcWidth, srcHeight, Gdiplus::Unit::UnitPixel);
     hr = GdipHresultFromStatus(gs);
     if (SUCCEEDED(hr))
     {
@@ -4218,11 +4411,6 @@ static void FreeControl(
         ReleaseStr(pControl->sczValue);
         ReleaseStr(pControl->sczVariable);
 
-        if (pControl->pBitmap)
-        {
-            delete pControl->pBitmap;
-        }
-
         if (pControl->hImage)
         {
             ::DeleteBitmap(pControl->hImage);
@@ -4351,6 +4539,17 @@ static void FreeFont(
 }
 
 
+static void FreeImageInstance(
+    __in THEME_IMAGE_INSTANCE* pImageInstance
+    )
+{
+    if (pImageInstance->pBitmap)
+    {
+        delete pImageInstance->pBitmap;
+    }
+}
+
+
 static DWORD CALLBACK RichEditStreamFromFileHandleCallback(
     __in DWORD_PTR dwCookie,
     __in_bcount(cb) LPBYTE pbBuff,
@@ -5298,6 +5497,7 @@ static HRESULT LoadControls(
         LPCWSTR wzWindowClass = NULL;
         DWORD dwWindowBits = WS_CHILD;
         DWORD dwWindowExBits = 0;
+        BOOL fOwnerDrawImage = THEME_IMAGE_REFERENCE_TYPE_NONE != pControl->imageRef.type;
 
         if (fStartNewGroup)
         {
@@ -5322,7 +5522,7 @@ static HRESULT LoadControls(
             __fallthrough;
         case THEME_CONTROL_TYPE_BUTTON:
             wzWindowClass = WC_BUTTONW;
-            if (pControl->pBitmap || (pTheme->pBitmap && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
+            if (fOwnerDrawImage)
             {
                 dwWindowBits |= BS_OWNERDRAW;
                 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
@@ -5356,7 +5556,7 @@ static HRESULT LoadControls(
             break;
 
         case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps).
-            if (pControl->pBitmap || (pTheme->pBitmap && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
+            if (fOwnerDrawImage)
             {
                 wzWindowClass = THEME_WC_STATICOWNERDRAW;
                 dwWindowBits |= SS_OWNERDRAW;
@@ -5383,7 +5583,7 @@ static HRESULT LoadControls(
             break;
 
         case THEME_CONTROL_TYPE_PROGRESSBAR:
-            if (pControl->pBitmap || (pTheme->pBitmap && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
+            if (fOwnerDrawImage)
             {
                 wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control.
                 dwWindowBits |= SS_OWNERDRAW;
-- 
cgit v1.2.3-55-g6feb