windows-nt/Source/XPSP1/NT/shell/themes/uxtheme/render.cpp
2020-09-26 16:20:57 +08:00

1462 lines
39 KiB
C++

//---------------------------------------------------------------------------
// Render.cpp - implements the themed drawing services
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "Render.h"
#include "Utils.h"
#include "Parser.h"
#include "Loader.h"
#include "tmutils.h"
#include "gradient.h"
#include "rgn.h"
#include "info.h"
#include "cache.h"
#include "cachelist.h"
#include "borderfill.h"
#include "imagefile.h"
#ifdef DEBUG
static DWORD s_dwSize = 0;
#endif
//---------------------------------------------------------------------------
HRESULT CreateRenderObj(CUxThemeFile *pThemeFile, int iCacheSlot, int iThemeOffset,
int iClassNameOffset, __int64 iUniqueId, BOOL fEnableCache, CDrawBase *pBaseObj,
CTextDraw *pTextObj, DWORD dwOtdFlags, CRenderObj **ppObj)
{
HRESULT hr = S_OK;
CRenderObj *pRender = new CRenderObj(pThemeFile, iCacheSlot, iThemeOffset,
iClassNameOffset, iUniqueId, fEnableCache, dwOtdFlags);
if (! pRender)
{
hr = MakeError32(E_OUTOFMEMORY);
}
else
{
hr = pRender->Init(pBaseObj, pTextObj);
if (FAILED(hr))
delete pRender;
else
*ppObj = pRender;
}
return hr;
}
//---------------------------------------------------------------------------
CRenderObj::CRenderObj(CUxThemeFile *pThemeFile, int iCacheSlot, int iThemeOffset,
int iClassNameOffset, __int64 iUniqueId, BOOL fEnableCache, DWORD dwOtdFlags)
{
strcpy(_szHead, "rendobj");
strcpy(_szTail, "end");
_fCacheEnabled = fEnableCache;
_fCloseThemeFile = FALSE;
_dwOtdFlags = dwOtdFlags;
if (pThemeFile)
{
if (SUCCEEDED(BumpThemeFileRefCount(pThemeFile)))
_fCloseThemeFile = TRUE;
}
_pThemeFile = pThemeFile;
_iCacheSlot = iCacheSlot;
_iUniqueId = iUniqueId;
if (pThemeFile)
{
_pbThemeData = pThemeFile->_pbThemeData;
_pbSectionData = _pbThemeData + iThemeOffset;
_ptm = GetThemeMetricsPtr(pThemeFile);
}
else
{
_pbThemeData = NULL;
_pbSectionData = NULL;
_ptm = NULL;
}
_pszClassName = ThemeString(pThemeFile, iClassNameOffset);
_iMaxPart = 0;
_pParts = NULL;
_iDpiOverride = 0;
//---- caller must call "Init()" after ctr! ----
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::PrepareAlphaBitmap(HBITMAP hBitmap)
{
HRESULT hr = S_OK;
//---- convert to DIBDATA ----
CBitmapPixels pixels;
DWORD *pPixelQuads;
int iWidth, iHeight, iBytesPerPixel, iBytesPerRow;
hr = pixels.OpenBitmap(NULL, hBitmap, TRUE, &pPixelQuads, &iWidth, &iHeight,
&iBytesPerPixel, &iBytesPerRow);
if (FAILED(hr))
goto exit;
PreMultiplyAlpha(pPixelQuads, iWidth, iHeight);
pixels.CloseBitmap(NULL, hBitmap);
exit:
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::Init(CDrawBase *pBaseObj, CTextDraw *pTextObj)
{
HRESULT hr = S_OK;
if (_fCacheEnabled)
{
hr = BuildPackedPtrs(pBaseObj, pTextObj);
if (FAILED(hr))
goto exit;
}
//---- prepare direct objects ----
if ((pBaseObj) && (pBaseObj->_eBgType == BT_IMAGEFILE))
{
CMaxImageFile *pMaxIf = (CMaxImageFile *)pBaseObj;
//---- process primary image ----
DIBINFO *pdi = &pMaxIf->_ImageInfo;
if (pdi->fAlphaChannel)
{
hr = PrepareAlphaBitmap(pdi->hProcessBitmap);
if (FAILED(hr))
goto exit;
}
//---- process glyph image ----
pdi = &pMaxIf->_GlyphInfo;
if (pdi->fAlphaChannel)
{
hr = PrepareAlphaBitmap(pdi->hProcessBitmap);
if (FAILED(hr))
goto exit;
}
//---- process multiple images ----
for (int i=0; i < pMaxIf->_iMultiImageCount; i++)
{
pdi = pMaxIf->MultiDibPtr(i);
if (pdi->fAlphaChannel)
{
hr = PrepareAlphaBitmap(pdi->hProcessBitmap);
if (FAILED(hr))
goto exit;
}
}
}
exit:
return hr;
}
//---------------------------------------------------------------------------
CRenderObj::~CRenderObj()
{
//---- delete memory allocated for pack objects looked ----
if (_pParts)
{
for(int i=0; i<_iMaxPart+1; i++)
{
if (_pParts[i].pStateDrawObjs)
delete[] _pParts[i].pStateDrawObjs;
if (_pParts[i].pStateTextObjs)
delete[] _pParts[i].pStateTextObjs;
}
delete[] _pParts;
}
//---- if we opened a refcount on a themefile, close it now ----
if (_fCloseThemeFile)
CloseThemeFile(_pThemeFile);
//---- mark this object as "deleted" (for debugging) ----
strcpy(_szHead, "deleted");
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::SetDpiOverride(int iDpiOverride)
{
_iDpiOverride = iDpiOverride;
return S_OK;
}
//---------------------------------------------------------------------------
int CRenderObj::GetDpiOverride()
{
return _iDpiOverride;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::BuildPackedPtrs(CDrawBase *pBaseObj, CTextDraw *pTextObj)
{
MIXEDPTRS u;
HRESULT hr = S_OK;
int iPackedOffset = 0;
int *iPartOffsets = NULL;
BOOL fSingleObj = FALSE;
//---- extract _iMaxPart ----
if ((pBaseObj) || (pTextObj)) // single object to be used for all parts/states
{
_iMaxPart = 1; // dummy value
fSingleObj = TRUE;
}
else
{
u.pb = _pbSectionData;
if (*u.ps != TMT_PARTJUMPTABLE)
{
hr = MakeError32(E_FAIL); // something went amiss
goto exit;
}
u.pb += ENTRYHDR_SIZE;
iPackedOffset = *u.pi++;
_iMaxPart = *u.pb - 1;
u.pb++;
iPartOffsets = u.pi;
}
//---- allocate _pParts ----
_pParts = new PARTINFO[_iMaxPart+1];
if (! _pParts)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
memset(_pParts, 0, sizeof(PARTINFO)*(_iMaxPart+1));
if (fSingleObj)
{
for (int i=0; i <= _iMaxPart; i++)
_pParts[i].iMaxState = 1; // dummy value
if (pBaseObj) // single draw object to be used for all parts/states
{
for (int i=0; i <= _iMaxPart; i++)
{
_pParts[i].pDrawObj = pBaseObj;
}
}
if (pTextObj) // single text object t to be used for all parts/states
{
for (int i=0; i <= _iMaxPart; i++)
{
_pParts[i].pTextObj = pTextObj;
}
}
}
else
{
u.pb = _pbThemeData + iPackedOffset;
hr = WalkDrawObjects(u, iPartOffsets);
if (FAILED(hr))
goto exit;
hr = WalkTextObjects(u, iPartOffsets);
if (FAILED(hr))
goto exit;
}
exit:
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::WalkDrawObjects(MIXEDPTRS &u, int *iPartOffsets)
{
int iPartId;
int iStateId;
HRESULT hr = S_OK;
THEMEHDR *pHdr = (THEMEHDR *)_pbThemeData;
UNPACKED_ENTRYHDR hdr;
//---- get ptr to global text obj ----
BYTE *pb = _pbThemeData + pHdr->iGlobalsDrawObjOffset;
pb += ENTRYHDR_SIZE + sizeof(DRAWOBJHDR);
CDrawBase *pGlobalObj = (CDrawBase *)pb;
//---- start with all parts inheriting from [globals] ----
for (int i=0; i <= _iMaxPart; i++)
_pParts[i].pDrawObj = pGlobalObj;
//---- now, process all specified objects ----
while (1)
{
if ((*u.ps == TMT_RGNLIST) || (*u.ps == TMT_STOCKBRUSHES))
{
//---- skip over this entry ----
FillAndSkipHdr(u, &hdr);
u.pb += hdr.dwDataLen;
continue;
}
if (*u.ps != TMT_DRAWOBJ)
break;
FillAndSkipHdr(u, &hdr);
DRAWOBJHDR *ph = (DRAWOBJHDR *)u.pb;
CDrawBase *pCurrentObj = (CDrawBase *)(u.pb + sizeof(DRAWOBJHDR));
u.pb += hdr.dwDataLen;
iPartId = ph->iPartNum;
iStateId = ph->iStateNum;
if ((! iPartId) && (! iStateId))
{
//---- all parts inherit from this obj ----
for (int i=0; i <= _iMaxPart; i++)
_pParts[i].pDrawObj = pCurrentObj;
continue;
}
PARTINFO *ppi = &_pParts[iPartId];
if (! iStateId)
{
ppi->pDrawObj = pCurrentObj;
}
else
{
if (! ppi->iMaxState) // extract MaxState
{
MIXEDPTRS u2;
u2.pb = _pbThemeData + iPartOffsets[iPartId];
if (*u2.ps != TMT_STATEJUMPTABLE)
{
hr = MakeError32(E_FAIL); // something went amiss
goto exit;
}
u2.pb += ENTRYHDR_SIZE;
ppi->iMaxState = *u2.pb - 1;
}
if (! ppi->pStateDrawObjs) // allocate now
{
ppi->pStateDrawObjs = new CDrawBase *[ppi->iMaxState];
if (! ppi->pStateDrawObjs)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
//---- fill in default objs as state 0 ----
for (int i=0; i < ppi->iMaxState; i++)
ppi->pStateDrawObjs[i] = ppi->pDrawObj;
}
ppi->pStateDrawObjs[iStateId-1] = pCurrentObj;
}
}
exit:
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::WalkTextObjects(MIXEDPTRS &u, int *iPartOffsets)
{
int iPartId;
int iStateId;
HRESULT hr = S_OK;
THEMEHDR *pHdr = (THEMEHDR *)_pbThemeData;
UNPACKED_ENTRYHDR hdr;
//---- get ptr to global text obj ----
BYTE *pb = _pbThemeData + pHdr->iGlobalsTextObjOffset;
pb += ENTRYHDR_SIZE + sizeof(DRAWOBJHDR);
CTextDraw *pGlobalObj = (CTextDraw *)pb;
//---- start with all parts inheriting from [globals] ----
for (int i=0; i <= _iMaxPart; i++)
_pParts[i].pTextObj = pGlobalObj;
while (*u.ps == TMT_TEXTOBJ)
{
FillAndSkipHdr(u, &hdr);
DRAWOBJHDR *ph = (DRAWOBJHDR *)u.pb;
CTextDraw *pCurrentObj = (CTextDraw *)(u.pb + sizeof(DRAWOBJHDR));
u.pb += hdr.dwDataLen;
iPartId = ph->iPartNum;
iStateId = ph->iStateNum;
if ((! iPartId) && (! iStateId))
{
//---- all parts inherit from this obj ----
for (int i=0; i <= _iMaxPart; i++)
_pParts[i].pTextObj = pCurrentObj;
continue;
}
PARTINFO *ppi = &_pParts[iPartId];
if (! iStateId)
{
ppi->pTextObj = pCurrentObj;
}
else
{
if (! ppi->iMaxState) // extract MaxState
{
MIXEDPTRS u2;
u2.pb = _pbThemeData + iPartOffsets[iPartId];
if (*u2.ps != TMT_STATEJUMPTABLE)
{
hr = MakeError32(E_FAIL); // something went amiss
goto exit;
}
u2.pb += ENTRYHDR_SIZE;
ppi->iMaxState = *u2.pb - 1;
}
if (! ppi->pStateTextObjs) // allocate now
{
ppi->pStateTextObjs = new CTextDraw *[ppi->iMaxState];
if (! ppi->pStateTextObjs)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
//---- fill in default objs as state 0 ----
for (int i=0; i < ppi->iMaxState; i++)
ppi->pStateTextObjs[i] = ppi->pTextObj;
}
ppi->pStateTextObjs[iStateId-1] = pCurrentObj;
}
}
exit:
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetBitmap(HDC hdc, int iDibOffset, OUT HBITMAP *phBitmap)
{
HRESULT hr = S_OK;
HBITMAP hBitmap;
if ((! iDibOffset) || (! _pbThemeData))
{
hr = E_FAIL;
goto exit;
}
TMBITMAPHEADER *pThemeBitmapHeader;
pThemeBitmapHeader = reinterpret_cast<TMBITMAPHEADER*>(_pbThemeData + iDibOffset);
ASSERT(pThemeBitmapHeader->dwSize == TMBITMAPSIZE);
*phBitmap = pThemeBitmapHeader->hBitmap;
if (*phBitmap != NULL)
{
//Log(LOG_TMBITMAP, L"Used stock bitmap:%8X", *phBitmap);
return hr;
}
hr = CreateBitmapFromData(hdc, iDibOffset + TMBITMAPSIZE, &hBitmap);
if (FAILED(hr))
goto exit;
Log(LOG_TM, L"GetBitmap - CACHE MISS: class=%s, diboffset=%d, bitmap=0x%x",
SHARECLASS(this), iDibOffset, hBitmap);
#if 0
if (lstrcmpi(SHARECLASS(this), L"progress")==0)
{
//---- validate the bitmap ----
int iBytes = GetObject(hBitmap, 0, NULL);
Log(LOG_RFBUG, L"progress: CREATE bitmap, diboff=%d, hbitmap=0x%x, iBytes=%d",
iDibOffset, hBitmap, iBytes);
}
#endif
*phBitmap = hBitmap;
exit:
return hr;
}
//---------------------------------------------------------------------------
void CRenderObj::ReturnBitmap(HBITMAP hBitmap)
{
DeleteObject(hBitmap);
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::CreateBitmapFromData(HDC hdc, int iDibOffset, OUT HBITMAP *phBitmap)
{
BYTE *pDibData;
RESOURCE HDC hdcTemp = NULL;
RESOURCE HBITMAP hBitmap = NULL;
HRESULT hr = S_OK;
if ((! iDibOffset) || (! _pbThemeData))
{
hr = E_FAIL;
goto exit;
}
pDibData = (BYTE *)(_pbThemeData + iDibOffset);
BITMAPINFOHEADER *pBitmapHdr;
pBitmapHdr = (BITMAPINFOHEADER *)pDibData;
BOOL fAlphaChannel;
fAlphaChannel = (pBitmapHdr->biBitCount == 32);
if (! hdc)
{
hdcTemp = GetWindowDC(NULL);
if (! hdcTemp)
{
Log(LOG_ALWAYS, L"GetWindowDC() failed in CreateBitmapFromData");
hr = MakeErrorLast();
goto exit;
}
hdc = hdcTemp;
}
//---- create the actual bitmap ----
//---- if using alpha channel, we must use a DIB ----
if (fAlphaChannel)
{
void *pv;
hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)pBitmapHdr, DIB_RGB_COLORS,
&pv, NULL, 0);
}
else
{
hBitmap = CreateCompatibleBitmap(hdc, pBitmapHdr->biWidth, pBitmapHdr->biHeight);
}
if (! hBitmap)
{
hr = MakeErrorLast();
goto exit;
}
int iSetVal;
//---- SetDIBits() can take unaligned data, right? ----
iSetVal = SetDIBits(hdc, hBitmap, 0, pBitmapHdr->biHeight, DIBDATA(pBitmapHdr), (BITMAPINFO *)pBitmapHdr,
DIB_RGB_COLORS);
if (! iSetVal)
{
hr = MakeErrorLast();
goto exit;
}
*phBitmap = hBitmap;
#ifdef DEBUG
if (hBitmap)
{
BITMAP bm;
GetObject(hBitmap, sizeof bm, &bm);
s_dwSize += bm.bmWidthBytes * bm.bmHeight;
//Log(LOG_TMBITMAP, L"Created a bitmap of %d bytes. total is %d", bm.bmWidthBytes * bm.bmHeight, s_dwSize);
}
#endif
exit:
if (hdcTemp)
ReleaseDC(NULL, hdcTemp);
if (FAILED(hr))
{
if (hBitmap)
DeleteObject(hBitmap);
}
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetScaledFontHandle(HDC hdc, LOGFONT *plf, HFONT *phFont)
{
HRESULT hr = S_OK;
if (_fCacheEnabled)
{
CRenderCache *pCacheObj = GetTlsCacheObj();
if (pCacheObj)
hr = pCacheObj->GetScaledFontHandle(hdc, plf, phFont);
}
else
{
LOGFONT lf = *plf;
//---- convert to current screen dpi ----
ScaleFontForHdcDpi(hdc, &lf);
*phFont = CreateFontIndirect(&lf);
if (! *phFont)
hr = MakeError32(E_OUTOFMEMORY);
}
return hr;
}
//---------------------------------------------------------------------------
void CRenderObj::ReturnFontHandle(HFONT hFont)
{
if (_fCacheEnabled)
{
//--- cache currently doesn't refcnt so save time by not calling ---
//CRenderCache *pCacheObj = GetTlsCacheObj();
//if (pCacheObj)
//{
//pCacheObj->ReturnFontHandle(hFont);
//goto exit;
//}
}
else
{
DeleteObject(hFont);
}
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::PrepareRegionDataForScaling(
RGNDATA *pRgnData, LPCRECT prcImage, MARGINS *pMargins)
{
//---- compute margin values ----
int sw = prcImage->left;
int lw = prcImage->left + pMargins->cxLeftWidth;
int rw = prcImage->right - pMargins->cxRightWidth;
int sh = prcImage->top;
int th = prcImage->top + pMargins->cyTopHeight;
int bh = prcImage->bottom - pMargins->cyBottomHeight;
//---- step thru region data & customize it ----
//---- classify each POINT according to a gridnum and ----
//---- make it 0-relative to its grid location ----
POINT *pt = (POINT *)pRgnData->Buffer;
BYTE *pByte = (BYTE *)pRgnData->Buffer + pRgnData->rdh.nRgnSize;
int iCount = 2 * pRgnData->rdh.nCount;
for (int i=0; i < iCount; i++, pt++, pByte++)
{
if (pt->x < lw)
{
pt->x -= sw;
if (pt->y < th) // left top
{
*pByte = GN_LEFTTOP;
pt->y -= sh;
}
else if (pt->y < bh) // left middle
{
*pByte = GN_LEFTMIDDLE;
pt->y -= th;
}
else // left bottom
{
*pByte = GN_LEFTBOTTOM;
pt->y -= bh;
}
}
else if (pt->x < rw)
{
pt->x -= lw;
if (pt->y < th) // middle top
{
*pByte = GN_MIDDLETOP;
pt->y -= sh;
}
else if (pt->y < bh) // middle middle
{
*pByte = GN_MIDDLEMIDDLE;
pt->y -= th;
}
else // middle bottom
{
*pByte = GN_MIDDLEBOTTOM;
pt->y -= bh;
}
}
else
{
pt->x -= rw;
if (pt->y < th) // right top
{
*pByte = GN_RIGHTTOP;
pt->y -= sh;
}
else if (pt->y < bh) // right middle
{
*pByte = GN_RIGHTMIDDLE;
pt->y -= th;
}
else // right bottom
{
*pByte = GN_RIGHTBOTTOM;
pt->y -= bh;
}
}
}
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::CreateImageBrush(HDC hdc, int iPartId, int iStateId,
int iImageIndex, HBRUSH *phbr)
{
#if 0
HRESULT hr;
RESOURCE HDC dc2 = NULL;
RESOURCE HBITMAP hOldBitmap2 = NULL;
HBRUSH hBrush = NULL;
//---- get our bitmap from the cache ----
RESOURCE HBITMAP hBitmap;
hr = GetBitmap(hdc, iPartId, iStateId, &hBitmap);
if (FAILED(hr))
goto exit;
int iImageCount;
if (FAILED(GetInt(iPartId, iStateId, TMT_IMAGECOUNT, &iImageCount)))
iImageCount = 1; /// default value
if (iImageCount == 1) // do easy case first
{
hBrush = CreatePatternBrush(hBitmap);
goto gotit;
}
//---- create "dc2" to make our bitmap usable ----
dc2 = CreateCompatibleDC(hdc);
if (! dc2)
{
hr = MakeErrorLast();
goto exit;
}
hOldBitmap2 = (HBITMAP) SelectObject(dc2, hBitmap);
//---- create a sub-bitmap of just our target image in "memoryDC" ----
int width, height, xoffset, yoffset;
GetImageInfo(hBitmap, iPartId, iStateId, iImageIndex, &width, &height, &xoffset, &yoffset);
//---- local block ----
{
CMemoryDC memoryDC;
hr = memoryDC.OpenDC(dc2, width, height);
if (FAILED(hr))
return hr;
BitBlt(memoryDC, 0, 0, width, height, dc2, xoffset, yoffset, SRCCOPY);
//---- finally, create our brush ----
hBrush = CreatePatternBrush(memoryDC._hBitmap);
}
gotit:
if (hBrush)
{
*phbr = hBrush;
hr = S_OK;
}
else
hr = MakeErrorLast();
exit:
//---- now clean up all resources ----
if (dc2)
{
SelectObject(dc2, hOldBitmap2);
DeleteDC(dc2);
}
ReturnBitmap(hBitmap);
return hr;
#endif
return E_NOTIMPL;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetColor(int iPartId, int iStateId, int iPropId, COLORREF *pColor)
{
if (! pColor)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0) // not found
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
*pColor = *u.pi;
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetString(int iPartId, int iStateId, int iPropId,
LPWSTR pszBuff, DWORD dwMaxBuffChars)
{
if (! pszBuff)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index - sizeof(int); // point at length
DWORD len = *u.pdw++;
len /= sizeof(WCHAR); // adjust to characters
HRESULT hr = hr_lstrcpy(pszBuff, u.pw, dwMaxBuffChars);
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetBool(int iPartId, int iStateId, int iPropId, BOOL *pfVal)
{
if (! pfVal)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
*pfVal = *u.pb;
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetInt(int iPartId, int iStateId, int iPropId, int *piVal)
{
if (! piVal)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
*piVal = *u.pi;
return S_OK;
}
//---------------------------------------------------------------------------
static int iMetricDefaults[] =
{
1, // TMT_BORDERWIDTH
18, // TMT_VERTSCROLLWIDTH
18, // TMT_HORZSCROLLHEIGHT
27, // TMT_CAPTIONBUTTONWIDTH
27, // TMT_CAPTIONBUTTONHEIGHT
22, // TMT_SMCAPTIONBUTTONWIDTH
22, // TMT_SMCAPTIONBUTTONHEIGHT
22, // TMT_MENUBUTTONWIDTH
22, // TMT_MENUBUTTONHEIGHT
};
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetMetric(OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId, int *piVal)
{
if (! piVal)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
int value;
if (index >= 0) // found
{
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
value = *u.pi;
}
else
return MakeError32(ERROR_NOT_FOUND);
*piVal = ScaleSizeForHdcDpi(hdc, value);
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetEnumValue(int iPartId, int iStateId, int iPropId, int *piVal)
{
if (! piVal)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
*piVal = *u.pi;
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetPosition(int iPartId, int iStateId, int iPropId, POINT *pPoint)
{
if (! pPoint)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
pPoint->x = *u.pi++;
pPoint->y = *u.pi++;
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetFont(OPTIONAL HDC hdc, int iPartId, int iStateId, int iPropId,
BOOL fWantHdcScaling, LOGFONT *pFont)
{
if (! pFont)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
*pFont = *(LOGFONT *)u.pb;
if (fWantHdcScaling)
{
ScaleFontForHdcDpi(hdc, pFont);
}
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetMargins(OPTIONAL HDC hdc, int iPartId, int iStateId,
int iPropId, OPTIONAL RECT *prc, MARGINS *pMargins)
{
//---- return unscaled margins ----
if (! pMargins)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
pMargins->cxLeftWidth = *u.pi++;
pMargins->cxRightWidth = *u.pi++;
pMargins->cyTopHeight = *u.pi++;
pMargins->cyBottomHeight = *u.pi++;
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetIntList(int iPartId, int iStateId, int iPropId, INTLIST *pIntList)
{
if (! pIntList)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
int iCount = *u.pi++;
if (iCount > MAX_INTLIST_COUNT)
{
Log(LOG_ALWAYS, L"GetIntList() found bad theme data - Count=%d", iCount);
return MakeError32(ERROR_NOT_FOUND);
}
pIntList->iValueCount = iCount;
for (int i=0; i < iCount; i++)
pIntList->iValues[i] = *u.pi++;
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetRect(int iPartId, int iStateId, int iPropId, RECT *pRect)
{
if (! pRect)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index; // point at data
pRect->left = *u.pi++;
pRect->top = *u.pi++;
pRect->right = *u.pi++;
pRect->bottom = *u.pi++;
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetFilenameOffset(int iPartId, int iStateId, int iPropId,
int *piFileNameOffset)
{
if (! piFileNameOffset)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
*piFileNameOffset = index; // offset to filename
return S_OK;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetFilename(int iPartId, int iStateId, int iPropId, LPWSTR pszBuff,
DWORD dwMaxBuffChars)
{
if (! pszBuff)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index - sizeof(int); // point at length
DWORD len = *u.pdw++;
len /= sizeof(WCHAR); // adjust to chars size
HRESULT hr = hr_lstrcpy(pszBuff, u.pw, dwMaxBuffChars);
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetData(int iPartId, int iStateId, int iPropId, BYTE **ppData,
OPTIONAL int *piSize)
{
if (! ppData)
return MakeError32(E_INVALIDARG);
int index = GetValueIndex(iPartId, iStateId, iPropId);
if (index < 0)
return MakeError32(ERROR_NOT_FOUND);
MIXEDPTRS u;
u.pb = _pbThemeData + index - sizeof(int); // point at length
DWORD len = *u.pdw++;
*ppData = u.pb;
if (piSize)
*piSize = len;
return S_OK;
}
//---------------------------------------------------------------------------
int CRenderObj::GetValueIndex(int iPartId, int iStateId, int iTarget)
{
if (! iTarget)
{
Log(LOG_PARAMS, L"Invalid iProperyId passed to GetValueIndex: %d", iTarget);
return -1;
}
if (! _pbSectionData)
{
return -1;
}
MIXEDPTRS u;
int index;
u.pb = _pbSectionData;
//---- find end of data ----
THEMEHDR *hdr = (THEMEHDR *)_pbThemeData;
BYTE *pbLastValidChar = _pbThemeData + (hdr->dwTotalLength - 1) - kcbEndSignature;
while (u.pb <= pbLastValidChar)
{
UNPACKED_ENTRYHDR hdr;
FillAndSkipHdr(u, &hdr);
if (hdr.usTypeNum == TMT_PARTJUMPTABLE)
{
u.pi++; // skip over offset to first drawobj
BYTE cnt = *u.pb++;
if ((iPartId < 0) || (iPartId >= cnt))
{
index = u.pi[0];
}
else
{
index = u.pi[iPartId];
if (index == -1)
index = u.pi[0];
}
u.pb = _pbThemeData + index;
continue;
}
if (hdr.usTypeNum == (BYTE)TMT_STATEJUMPTABLE)
{
BYTE cnt = *u.pb++;
if ((iStateId < 0) || (iStateId >= cnt))
index = u.pi[0];
else
{
index = u.pi[iStateId];
if (index == -1)
index = u.pi[0];
}
u.pb = _pbThemeData + index;
continue;
}
if (hdr.usTypeNum == iTarget) // got our target
{
// Log("GetValueIndex: match at index=%d", u.pb - _pbThemeData);
return (int)(u.pb - _pbThemeData); // point at actual data (not hdr)
}
if (hdr.ePrimVal == TMT_JUMPTOPARENT)
{
index = *u.pi;
if (index == -1)
{
// Log("no match found");
return -1;
}
// Log("GetValueIndex: jumping to parent at index=%d", index);
u.pb = _pbThemeData + index;
continue;
}
// Log("GetValueIndex: no match to hdr.usTypeNum=%d", hdr.usTypeNum);
// advance to next value
u.pb += hdr.dwDataLen;
}
//---- something went wrong ----
Log(LOG_ERROR, L"GetValueIndex: ran off the valid data without a '-1' jump");
return -1;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetPropertyOrigin(int iPartId, int iStateId, int iTarget,
PROPERTYORIGIN *pOrigin)
{
if (! iTarget)
{
Log(LOG_PARAMS, L"Invalid iProperyId passed to GetPropertyOrigin: %d", iTarget);
return E_FAIL;
}
if (! _pbSectionData)
{
return E_FAIL;
}
MIXEDPTRS u;
if (! pOrigin)
return MakeError32(E_INVALIDARG);
//---- start at our section ----
u.pb = _pbSectionData;
PROPERTYORIGIN origin = PO_CLASS;
//---- find end of data ----
THEMEHDR *hdr = (THEMEHDR *)_pbThemeData;
BYTE *pbLastValidChar = _pbThemeData + (hdr->dwTotalLength - 1) - kcbEndSignature;
while (u.pb <= pbLastValidChar)
{
UNPACKED_ENTRYHDR hdr;
FillAndSkipHdr(u, &hdr);
if (hdr.usTypeNum == TMT_PARTJUMPTABLE)
{
u.pi++; // skip over offset to first drawobj
BYTE cnt = *u.pb++;
int index;
if ((iPartId <= 0) || (iPartId >= cnt))
{
index = u.pi[0];
}
else
{
index = u.pi[iPartId];
if (index == -1)
index = u.pi[0];
}
if (index == u.pi[0])
origin = PO_CLASS;
else
origin = PO_PART;
u.pb = _pbThemeData + index;
continue;
}
if (hdr.usTypeNum == TMT_STATEJUMPTABLE)
{
BYTE cnt = *u.pb++;
int index;
if ((iStateId <= 0) || (iStateId >= cnt))
{
index = u.pi[0];
}
else
{
index = u.pi[iStateId];
if (index == -1)
index = u.pi[0];
}
if (index != u.pi[0])
origin = PO_STATE;
u.pb = _pbThemeData + index;
continue;
}
//Log("GetPropertyOrgin: iPartId=%d, iTarget=%d, DataIndex=%d",
// iPartId, iTarget, u.pb - _pbThemeData);
if ((iTarget == -1) || (hdr.usTypeNum == iTarget)) // got our target
{
// Log("GetPropertyOrgin: match at index=%d", u.pb - _pbThemeData);
*pOrigin = origin;
return S_OK;
}
if (hdr.ePrimVal == TMT_JUMPTOPARENT)
{
int index = *u.pi;
if (index == -1)
{
// Log("GetPropertyOrgin: no match found");
*pOrigin = PO_NOTFOUND;
return S_OK;
}
// Log("GetPropertyOrgin: jumping to parent at index=%d", index);
u.pb = _pbThemeData + index;
origin = (PROPERTYORIGIN) (origin + 1); // move down to next level of heirarchy
continue;
}
// advance to next value
u.pb += hdr.dwDataLen;
}
//---- something went wrong ----
Log(LOG_ERROR, L"GetPropertyOrigin: ran off the valid data without a '-1' jump");
return E_FAIL;
}
//---------------------------------------------------------------------------
BOOL WINAPI CRenderObj::IsPartDefined(int iPartId, int iStateId)
{
PROPERTYORIGIN origin;
HRESULT hr = GetPropertyOrigin(iPartId, iStateId, -1, &origin);
SET_LAST_ERROR(hr);
if (FAILED(hr))
{
return FALSE;
}
if (iStateId)
return (origin == PO_STATE);
return (origin == PO_PART);
}
//---------------------------------------------------------------------------
BOOL CRenderObj::ValidateObj()
{
BOOL fValid = TRUE;
//---- check object quickly ----
if ( (! this)
|| (ULONGAT(_szHead) != 'dner') // "rend"
|| (ULONGAT(&_szHead[4]) != 'jbo') // "obj"
|| (ULONGAT(_szTail) != 'dne')) // "end"
{
Log(LOG_ALWAYS, L"CRenderObj is corrupt, addr=0x%08x", this);
fValid = FALSE;
}
return fValid;
}
//---------------------------------------------------------------------------
CRenderCache *CRenderObj::GetTlsCacheObj()
{
HRESULT hr = S_OK;
CRenderCache *pCacheObj = NULL;
CCacheList *pcl = GetTlsCacheList(TRUE);
if (pcl)
hr = pcl->GetCacheObject(this, _iCacheSlot, &pCacheObj);
return pCacheObj;
}
//---------------------------------------------------------------------------
#if 0
HRESULT CRenderObj::DispatchDrawBg()
{
HRESULT hr = S_OK;
if (FAILED(GetEnumValue(iPartId, iStateId, TMT_BGTYPE, (int *)&eBgType)))
eBgType = BT_BORDERFILL; // default value
if (eBgType == BT_NTLFILE)
{
BYTE *pNtlData;
int iNtlLen;
hr = GetData(iPartId, iStateId, TMT_NTLDATA, &pNtlData, &iNtlLen);
if (FAILED(hr))
goto exit;
RECT rc = *pRect;
//---- need to get these from callers... ---
HBRUSH hbrDefault = NULL;
DWORD dwOptions = 0;
hr = RunNtl(hdc, rc, hbrDefault, dwOptions, iPartId, iStateId, pNtlData, iNtlLen, this);
goto exit;
}
return hr;
}
#endif
//---------------------------------------------------------------------------
HRESULT CRenderObj::FindGlobalDrawObj(BYTE *pb, int iPartId, int iStateId, CDrawBase **ppObj)
{
HRESULT hr = S_OK;
MIXEDPTRS u;
u.pb = pb;
BOOL fFound = FALSE;
UNPACKED_ENTRYHDR hdr;
while (1)
{
if ((*u.ps == TMT_RGNLIST) || (*u.ps == TMT_STOCKBRUSHES))
{
//---- skip over this entry ----
FillAndSkipHdr(u, &hdr);
u.pb += hdr.dwDataLen;
continue;
}
if (*u.ps != TMT_DRAWOBJ)
break;
FillAndSkipHdr(u, &hdr);
DRAWOBJHDR *ph = (DRAWOBJHDR *)u.pb;
if ((ph->iPartNum == iPartId) && (ph->iStateNum == iStateId))
{
*ppObj = (CDrawBase *)(u.pb + sizeof(DRAWOBJHDR));
fFound = TRUE;
break;
}
//---- skip over hdr+object ----
u.pb += hdr.dwDataLen;
}
if (! fFound)
hr = E_FAIL;
return hr;
}
//---------------------------------------------------------------------------
HRESULT CRenderObj::GetGlobalDrawObj(int iPartId, int iStateId, CDrawBase **ppObj)
{
//---- perf note: we don't currently cache the ptrs for LINE and BORDER ----
//---- global objects since they are not used much/at all ----
HRESULT hr = S_OK;
if (! _pbThemeData)
{
hr = E_FAIL;
goto exit;
}
THEMEHDR *pHdr;
pHdr = (THEMEHDR *)_pbThemeData;
//---- get ptr to global text obj ----
MIXEDPTRS u;
u.pb = _pbThemeData + pHdr->iGlobalsDrawObjOffset;
//---- first try exact match ---
hr = FindGlobalDrawObj(u.pb, iPartId, iStateId, ppObj);
if (FAILED(hr))
{
//---- look for state=0 ----
hr = FindGlobalDrawObj(u.pb, iPartId, 0, ppObj);
if (FAILED(hr))
{
//---- just use the global draw obj ----
if (*u.ps == TMT_DRAWOBJ)
{
u.pb += ENTRYHDR_SIZE + sizeof(DRAWOBJHDR);
*ppObj = (CDrawBase *)u.pb;
hr = S_OK;
}
}
}
exit:
return hr;
}
//---------------------------------------------------------------------------