//--------------------------------------------------------------------------- // 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(_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; } //---------------------------------------------------------------------------