//--------------------------------------------------------------------------- // ImageFile.cpp - implements the drawing API for bgtype = ImageFile //--------------------------------------------------------------------------- #include "stdafx.h" #include "Render.h" #include "Utils.h" #include "tmutils.h" #include "rgn.h" #include "ImageFile.h" #include "CacheList.h" #include "DrawHelp.h" #include "ninegrid2.h" #include "TmReg.h" #include "globals.h" #include "bmpcache.h" //--------------------------------------------------------------------------- void AdjustSizeMin(SIZE *psz, int ixMin, int iyMin) { if (psz->cx < ixMin) { psz->cx = ixMin; } if (psz->cy < iyMin) { psz->cy = iyMin; } } //--------------------------------------------------------------------------- HRESULT CMaxImageFile::PackMaxProperties(CRenderObj *pRender, int iPartId, int iStateId, OUT int *piMultiDibCount) { HRESULT hr = PackProperties(pRender, iPartId, iStateId); *piMultiDibCount = _iMultiImageCount; return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::PackProperties(CRenderObj *pRender, int iPartId, int iStateId) { HRESULT hr = S_OK; memset(this, 0, sizeof(CImageFile)); // allowed because we have no vtable _eBgType = BT_IMAGEFILE; //---- save off partid, stateid for debugging ---- _iSourcePartId = iPartId; _iSourceStateId = iStateId; DIBINFO *pdi = &_ImageInfo; pdi->iMinDpi = 96; // only way this gets set for now pdi->iDibOffset = pRender->GetValueIndex(iPartId, iStateId, TMT_DIBDATA); if (pdi->iDibOffset == -1) // not found pdi->iDibOffset = 0; //---- image-related fields ---- if (FAILED(pRender->GetInt(iPartId, iStateId, TMT_IMAGECOUNT, &_iImageCount))) _iImageCount = 1; // default value if (_iImageCount < 1) // avoid divide by zero problems _iImageCount = 1; if (FAILED(pRender->GetEnumValue(iPartId, iStateId, TMT_IMAGELAYOUT, (int *)&_eImageLayout))) _eImageLayout = IL_HORIZONTAL; // default value until we are converted if (pdi->iDibOffset) { //---- compute some fields from bitmap ---- hr = SetImageInfo(pdi, pRender, iPartId, iStateId); if (FAILED(hr)) goto exit; } //---- get MinSize ---- if (FAILED(pRender->GetPosition(iPartId, iStateId, TMT_MINSIZE, (POINT *)&pdi->szMinSize))) { pdi->szMinSize.cx = pdi->iSingleWidth; pdi->szMinSize.cy = pdi->iSingleHeight; } else { AdjustSizeMin(&pdi->szMinSize, 1, 1); } //---- get TrueSizeScalingType ---- if (FAILED(pRender->GetEnumValue(iPartId, iStateId, TMT_TRUESIZESCALINGTYPE, (int *)&_eTrueSizeScalingType))) _eTrueSizeScalingType = TSST_NONE; // default //---- sizing ---- if (FAILED(pRender->GetEnumValue(iPartId, iStateId, TMT_SIZINGTYPE, (int *)&pdi->eSizingType))) pdi->eSizingType = ST_STRETCH; // default if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_BORDERONLY, &pdi->fBorderOnly))) pdi->fBorderOnly = FALSE; if (FAILED(pRender->GetInt(iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &_iTrueSizeStretchMark))) _iTrueSizeStretchMark = 0; // default if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_UNIFORMSIZING, &_fUniformSizing))) _fUniformSizing = FALSE; // default if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_INTEGRALSIZING, &_fIntegralSizing))) _fIntegralSizing = FALSE; // default //---- transparency ---- if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_TRANSPARENT, &pdi->fTransparent))) pdi->fTransparent = FALSE; if (pdi->fTransparent) { if (FAILED(pRender->GetColor(iPartId, iStateId, TMT_TRANSPARENTCOLOR, &pdi->crTransparent))) pdi->crTransparent = DEFAULT_TRANSPARENT_COLOR; } //---- MirrorImage ---- if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_MIRRORIMAGE, &_fMirrorImage))) _fMirrorImage = TRUE; // default setting //---- alignment ---- if (FAILED(pRender->GetEnumValue(iPartId, iStateId, TMT_HALIGN, (int *)&_eHAlign))) _eHAlign = HA_CENTER; // default value if (FAILED(pRender->GetEnumValue(iPartId, iStateId, TMT_VALIGN, (int *)&_eVAlign))) _eVAlign = VA_CENTER; // default value //---- for regular or glyph truesize images ---- if (SUCCEEDED(pRender->GetBool(iPartId, iStateId, TMT_BGFILL, &_fBgFill))) { //---- get fill color ---- if (FAILED(pRender->GetColor(iPartId, iStateId, TMT_FILLCOLOR, &_crFill))) _crFill = RGB(255, 255, 255); } //---- SizingMargins ---- if (FAILED(pRender->GetMargins(NULL, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &_SizingMargins))) { _SizingMargins.cxLeftWidth = 0; _SizingMargins.cxRightWidth = 0; _SizingMargins.cyTopHeight = 0; _SizingMargins.cyBottomHeight = 0; } //---- ContentMargins ---- if (FAILED(pRender->GetMargins(NULL, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &_ContentMargins))) { _ContentMargins = _SizingMargins; } //---- SourceGrow ---- if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_SOURCEGROW, &_fSourceGrow))) _fSourceGrow = FALSE; // default //---- SourceShrink ---- if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_SOURCESHRINK, &_fSourceShrink))) _fSourceShrink = FALSE; // default //---- NormalSize ---- if (FAILED(pRender->GetPosition(iPartId, iStateId, TMT_NORMALSIZE, (POINT *)&_szNormalSize))) { _szNormalSize.cx = 60; _szNormalSize.cy = 30; } else { AdjustSizeMin(&_szNormalSize, 1, 1); } //---- glphytype ---- if (FAILED(pRender->GetEnumValue(iPartId, iStateId, TMT_GLYPHTYPE, (int *)&_eGlyphType))) _eGlyphType = GT_NONE; // default value if (_eGlyphType == GT_FONTGLYPH) { //---- font-based glyphs ---- if (FAILED(pRender->GetFont(NULL, iPartId, iStateId, TMT_GLYPHFONT, FALSE, &_lfGlyphFont))) goto exit; // required if (FAILED(pRender->GetColor(iPartId, iStateId, TMT_GLYPHTEXTCOLOR, &_crGlyphTextColor))) _crGlyphTextColor = RGB(0, 0, 0); // default color if (FAILED(pRender->GetInt(iPartId, iStateId, TMT_GLYPHINDEX, &_iGlyphIndex))) _iGlyphIndex = 1; // default index } else if (_eGlyphType == GT_IMAGEGLYPH) { //---- image-based glyphs ---- pdi = &_GlyphInfo; pdi->iMinDpi = 96; // only way this gets set for now pdi->iDibOffset = pRender->GetValueIndex(iPartId, iStateId, TMT_GLYPHDIBDATA); if (pdi->iDibOffset == -1) pdi->iDibOffset = 0; if (pdi->iDibOffset > 0) // found { hr = SetImageInfo(pdi, pRender, iPartId, iStateId); if (FAILED(hr)) goto exit; } if (SUCCEEDED(pRender->GetBool(iPartId, iStateId, TMT_GLYPHTRANSPARENT, &pdi->fTransparent))) { if (FAILED(pRender->GetColor(iPartId, iStateId, TMT_GLYPHTRANSPARENTCOLOR, &pdi->crTransparent))) pdi->crTransparent = DEFAULT_TRANSPARENT_COLOR; } pdi->eSizingType = ST_TRUESIZE; // glyphs are always true size pdi->fBorderOnly = FALSE; // glyphs are never borderonly (for now) } if (_eGlyphType != GT_NONE) { if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_GLYPHONLY, &_fGlyphOnly))) _fGlyphOnly = FALSE; } //---- multi files specified? ---- if (FAILED(pRender->GetEnumValue(iPartId, iStateId, TMT_IMAGESELECTTYPE, (int *)&_eImageSelectType))) _eImageSelectType = IST_NONE; //---- fill in multi DIBINFO's ---- if (_eImageSelectType != IST_NONE) { DIBINFO *pParent; if (_eGlyphType == GT_IMAGEGLYPH) { pParent = &_GlyphInfo; } else { pParent = &_ImageInfo; } for (int i=0; i < MAX_IMAGEFILE_SIZES; i++) { //---- get ImageFileN ---- int iDibOffset = pRender->GetValueIndex(iPartId, iStateId, TMT_DIBDATA1 + i); if (iDibOffset == -1) break; _iMultiImageCount++; DIBINFO *pdi = MultiDibPtr(i); *pdi = *pParent; // inherit some props from parent pdi->iDibOffset = iDibOffset; hr = SetImageInfo(pdi, pRender, iPartId, iStateId); if (FAILED(hr)) goto exit; //---- get MinDpiN ---- if (FAILED(pRender->GetInt(iPartId, iStateId, TMT_MINDPI1 + i, &pdi->iMinDpi))) { pdi->iMinDpi = 96; // default } else { //---- ensure value >= 1 ---- if (pdi->iMinDpi < 1) { pdi->iMinDpi = 1; } } //---- get MinSizeN ---- if (FAILED(pRender->GetPosition(iPartId, iStateId, TMT_MINSIZE1 + i, (POINT *)&pdi->szMinSize))) { pdi->szMinSize.cx = pdi->iSingleWidth; pdi->szMinSize.cy = pdi->iSingleHeight; } else { AdjustSizeMin(&pdi->szMinSize, 1, 1); } } if (_iMultiImageCount > 0) { *pParent = *MultiDibPtr(0); // use first multi entry as primary object } } exit: return hr; } //--------------------------------------------------------------------------- BOOL CImageFile::KeyProperty(int iPropId) { BOOL fKey = FALSE; switch (iPropId) { case TMT_BGTYPE: case TMT_TRANSPARENT: case TMT_AUTOSIZE: case TMT_BORDERONLY: case TMT_IMAGECOUNT: case TMT_ALPHALEVEL: case TMT_ALPHATHRESHOLD: case TMT_IMAGEFILE: case TMT_IMAGEFILE1: case TMT_IMAGEFILE2: case TMT_IMAGEFILE3: case TMT_IMAGEFILE4: case TMT_IMAGEFILE5: case TMT_SIZINGMARGINS: case TMT_CONTENTMARGINS: case TMT_TRANSPARENTCOLOR: case TMT_SIZINGTYPE: case TMT_HALIGN: case TMT_VALIGN: case TMT_IMAGELAYOUT: case TMT_BGFILL: case TMT_MIRRORIMAGE: case TMT_TRUESIZESTRETCHMARK: case TMT_TRUESIZESCALINGTYPE: case TMT_IMAGESELECTTYPE: case TMT_UNIFORMSIZING: case TMT_INTEGRALSIZING: case TMT_SOURCEGROW: case TMT_SOURCESHRINK: case TMT_NORMALSIZE: case TMT_MINSIZE: case TMT_MINSIZE1: case TMT_MINSIZE2: case TMT_MINSIZE3: case TMT_MINSIZE4: case TMT_MINSIZE5: case TMT_MINDPI1: case TMT_MINDPI2: case TMT_MINDPI3: case TMT_MINDPI4: case TMT_MINDPI5: //---- glyph properties ---- case TMT_GLYPHTYPE: case TMT_GLYPHIMAGEFILE: case TMT_GLYPHTRANSPARENT: case TMT_GLYPHTRANSPARENTCOLOR: case TMT_GLYPHFONT: case TMT_GLYPHINDEX: case TMT_GLYPHTEXTCOLOR: case TMT_GLYPHONLY: // case TMT_FILLCOLOR: - this prop belongs to BorderFill (we borrow it) fKey = TRUE; break; } return fKey; } //--------------------------------------------------------------------------- DIBINFO *CImageFile::EnumImageFiles(int iIndex) { DIBINFO *pdi = NULL; BOOL fHasGlyph = (_eGlyphType == GT_IMAGEGLYPH); //---- enum in this order: primary, glyph, multi images ---- if (iIndex == 0) { pdi = &_ImageInfo; } else if (iIndex == 1) { if (fHasGlyph) pdi = &_GlyphInfo; } if (! pdi) // not yet set { if (fHasGlyph) iIndex -= 2; else iIndex -= 1; if (iIndex < _iMultiImageCount) { pdi = MultiDibPtr(iIndex); } } return pdi; } //--------------------------------------------------------------------------- void CImageFile::DumpProperties(CSimpleFile *pFile, BYTE *pbThemeData, BOOL fFullInfo) { if (fFullInfo) pFile->OutLine(L"Dump of CImageFile at offset=0x%x", (BYTE *)this - pbThemeData); else pFile->OutLine(L"Dump of CImageFile"); pFile->OutLine(L" _eBgType=%d", _eBgType); DIBINFO *pdi = &_ImageInfo; if (fFullInfo) { pFile->OutLine(L" iDibOffset=%d, _iImageCount=%d, _eImageLayout=%d", pdi->iDibOffset, _iImageCount, _eImageLayout); } else { pFile->OutLine(L" _iImageCount=%d, _eImageLayout=%d, MinSize=(%d, %d)", _iImageCount, _eImageLayout, pdi->szMinSize.cx, pdi->szMinSize.cy); } pFile->OutLine(L" _iSingleWidth=%d, _iSingleHeight=%d, _fMirrorImage=%d", pdi->iSingleWidth, pdi->iSingleHeight, _fMirrorImage); //---- dump multiple image info ---- for (int i=0; i < _iMultiImageCount; i++) { DIBINFO *pdi = MultiDibPtr(i); pFile->OutLine(L" Multi[%d]: sw=%d, sh=%d, diboff=%d, rgnoff=%d", i, pdi->iSingleWidth, pdi->iSingleHeight, (pdi->iDibOffset > 0), (pdi->iRgnListOffset > 0)); pFile->OutLine(L" MinDpi=%d, MinSize=(%d, %d)", pdi->iMinDpi, pdi->szMinSize.cx, pdi->szMinSize.cy); pFile->OutLine(L" sizetype=%d, bordonly=%d, fTrans=%d, crTrans=0x%x, fAlpha=%d, iAlphaThres=%d", pdi->eSizingType, pdi->fBorderOnly, pdi->fTransparent, pdi->crTransparent, pdi->fAlphaChannel, pdi->iAlphaThreshold); } pFile->OutLine(L" _eSizingType=%d, _fBorderOnly=%d, _eTrueSizeScalingType=%d", pdi->eSizingType, pdi->fBorderOnly, _eTrueSizeScalingType); pFile->OutLine(L" _fTransparent=%d, _crTransparent=0x%08x", pdi->fTransparent, pdi->crTransparent); pFile->OutLine(L" _fAlphaChannel=%d, _iAlphaThreshold=%d", pdi->fAlphaChannel, pdi->iAlphaThreshold); pFile->OutLine(L" _eHAlign=%d, _eVAlign=%d, _iTrueSizeStretchMark=%d", _eHAlign, _eVAlign, _iTrueSizeStretchMark); pFile->OutLine(L" _fUniformSizing=%d, _fIntegralSizing=%d", _fUniformSizing, _fIntegralSizing); pFile->OutLine(L" _fBgFill=%d, _crFill=0x%08x", _fBgFill, _crFill); pFile->OutLine(L" _fSourceGrow=%d, _fSourceShrink=%d, _szNormalSize=(%d, %d)", _fSourceGrow, _fSourceShrink, _szNormalSize.cx, _szNormalSize.cy); pFile->OutLine(L" _SizingMargins=%d, %d, %d, %d", _SizingMargins.cxLeftWidth, _SizingMargins.cxRightWidth, _SizingMargins.cyTopHeight, _SizingMargins.cyBottomHeight); pFile->OutLine(L" _ContentMargins=%d, %d, %d, %d", _ContentMargins.cxLeftWidth, _ContentMargins.cxRightWidth, _ContentMargins.cyTopHeight, _ContentMargins.cyBottomHeight); pFile->OutLine(L" _fFontGlyph=%d, _iGlyphIndex=%d, _crGlyphTextColor=0x%x", (_eGlyphType==GT_FONTGLYPH), _iGlyphIndex, _crGlyphTextColor); pFile->OutLine(L" _lfGlyphFont=%s, _fGlyphOnly=%d, _fImageGlyph=%d", _lfGlyphFont.lfFaceName, _fGlyphOnly, (_eGlyphType==GT_IMAGEGLYPH)); //---- dump glyph properties ---- pdi = &_GlyphInfo; if (fFullInfo) { pFile->OutLine(L" Glyph: iDibOffset=%d, iSingleWidth=%d, iSingleHeight=%d", pdi->iDibOffset, pdi->iSingleWidth, pdi->iSingleHeight); } else { pFile->OutLine(L" _iGlyphSingleWidth=%d, _iGlyphSingleHeight=%d", pdi->iSingleWidth, pdi->iSingleHeight); } pFile->OutLine(L" _fGlyphTransparent=%d, _crGlyphTransparent=0x%x, _fGlyphAlpha=%d", pdi->fTransparent, pdi->crTransparent, pdi->fAlphaChannel); //pFile->OutLine(L" Glyph: iAlphaThreshold=%d", pdi->iAlphaThreshold); } //--------------------------------------------------------------------------- HRESULT CImageFile::SetImageInfo(DIBINFO *pdi, CRenderObj *pRender, int iPartId, int iStateId) { HRESULT hr = S_OK; if (! pRender->_pbThemeData) { hr = E_FAIL; goto exit; } TMBITMAPHEADER *pThemeBitmapHeader = NULL; pThemeBitmapHeader = reinterpret_cast(pRender->_pbThemeData + pdi->iDibOffset); ASSERT(pThemeBitmapHeader->dwSize == TMBITMAPSIZE); pdi->fAlphaChannel = pThemeBitmapHeader->fTrueAlpha; if (pdi->fAlphaChannel) { if (FAILED(pRender->GetBool(iPartId, iStateId, TMT_ALPHATHRESHOLD, &pdi->iAlphaThreshold))) pdi->iAlphaThreshold = 255; } int iWidth = - 1; int iHeight = -1; if (pThemeBitmapHeader->hBitmap) { BITMAP bmInfo; if (GetObject(pThemeBitmapHeader->hBitmap, sizeof(bmInfo), &bmInfo)) { iWidth = bmInfo.bmWidth; iHeight = bmInfo.bmHeight; } else { hr = E_FAIL; } } else { BITMAPINFOHEADER* pbmInfo = BITMAPDATA(pThemeBitmapHeader); if (pbmInfo) { iWidth = pbmInfo->biWidth; iHeight = pbmInfo->biHeight; } else { hr = E_FAIL; } } //---- get SingleWidth/SingleHeight of bitmap ---- if ((iWidth != -1) && (iHeight != -1)) { if (_eImageLayout == IL_HORIZONTAL) { pdi->iSingleWidth = iWidth / _iImageCount; pdi->iSingleHeight = iHeight; } else // vertical { pdi->iSingleWidth = iWidth; pdi->iSingleHeight = iHeight / _iImageCount; } } exit: return hr; } //--------------------------------------------------------------------------- BOOL CImageFile::HasRegionImageFile(DIBINFO *pdi, int *piMaxState) { BOOL fGot = FALSE; if ((pdi->fTransparent) || (pdi->fAlphaChannel)) { if (pdi->iDibOffset > 0) { fGot = TRUE; *piMaxState = _iImageCount; } } return fGot; } //--------------------------------------------------------------------------- void CImageFile::SetRgnListOffset(DIBINFO *pdi, int iOffset) { //---- get offset to the actual jump table ---- pdi->iRgnListOffset = iOffset + ENTRYHDR_SIZE; } //--------------------------------------------------------------------------- HRESULT CImageFile::BuildRgnData(DIBINFO *pdi, CRenderObj *pRender, int iStateId, RGNDATA **ppRgnData, int *piDataLen) { RESOURCE HRGN hrgn = NULL; RESOURCE RGNDATA *pRgnData = NULL; int iTotalBytes = 0; int iRectCount; DWORD len, len2; HBITMAP hBitmap = NULL; HRESULT hr = S_OK; BOOL fStock = FALSE; if ((! pdi->fAlphaChannel) && (! pdi->fTransparent)) // empty region goto gotit; if (pRender->_pbThemeData && pdi->iDibOffset > 0) { fStock = ((reinterpret_cast(pRender->_pbThemeData + pdi->iDibOffset))->hBitmap != NULL); } hr = pRender->GetBitmap(NULL, pdi->iDibOffset, &hBitmap); if (FAILED(hr)) goto exit; int iXOffset, iYOffset; GetOffsets(iStateId, pdi, &iXOffset, &iYOffset); //---- create a region ---- hr = CreateBitmapRgn(hBitmap, iXOffset, iYOffset, pdi->iSingleWidth, pdi->iSingleHeight, pdi->fAlphaChannel, pdi->iAlphaThreshold, pdi->crTransparent, 0, &hrgn); if (FAILED(hr)) { //---- soft error - author said it was transparent but it wasn't ---- hr = S_OK; goto gotit; } //---- extract region data ---- len = GetRegionData(hrgn, 0, NULL); // get required length if (! len) { hr = MakeErrorLast(); goto exit; } iRectCount = len/sizeof(RECT); // # of rects len += ((sizeof(BYTE)+sizeof(BYTE))*iRectCount); // room for grid id's for each point iTotalBytes = len + sizeof(RGNDATAHEADER); pRgnData = (RGNDATA *) new BYTE[iTotalBytes]; len2 = GetRegionData(hrgn, len, pRgnData); if (! len2) { hr = MakeErrorLast(); goto exit; } //---- grid-ize the point values within each rect ---- RECT rcImage; SetRect( &rcImage, 0, 0, pdi->iSingleWidth, pdi->iSingleHeight ); hr = pRender->PrepareRegionDataForScaling(pRgnData, &rcImage, &_SizingMargins); if (FAILED(hr)) goto exit; gotit: *ppRgnData = pRgnData; *piDataLen = iTotalBytes; exit: if (hBitmap && !fStock) { pRender->ReturnBitmap(hBitmap); } if (hrgn) DeleteObject(hrgn); if (FAILED(hr)) { if (pRgnData) delete [] pRgnData; } return hr; } //--------------------------------------------------------------------------- // Helper function for DrawBackgroundDS void StreamSetSource(BYTE** pvStream, HBITMAP hbmSrc) { DS_SETSOURCE* pdsSetSource = (DS_SETSOURCE*)*pvStream; pdsSetSource->ulCmdID = DS_SETSOURCEID; pdsSetSource->hbm = HandleToULong(hbmSrc); *pvStream += sizeof(DS_SETSOURCE); } //--------------------------------------------------------------------------- void StreamInit(BYTE** pvStream, HDC hdcDest, HBITMAP hbmSrc, RECTL* prcl) { DS_HEADER* pdsHeader = (DS_HEADER*)*pvStream; pdsHeader->magic = DS_MAGIC; *pvStream += sizeof(DS_HEADER); DS_SETTARGET* pdsSetTarget = (DS_SETTARGET*)*pvStream; pdsSetTarget->ulCmdID = DS_SETTARGETID; pdsSetTarget->hdc = HandleToULong(hdcDest); pdsSetTarget->rclDstClip = *prcl; *pvStream += sizeof(DS_SETTARGET); StreamSetSource(pvStream, hbmSrc); } //--------------------------------------------------------------------------- HBITMAP CreateScaledTempBitmap(HDC hdc, HBITMAP hSrcBitmap, int ixSrcOffset, int iySrcOffset, int iSrcWidth, int iSrcHeight, int iDestWidth, int iDestHeight) { HBITMAP hTempBitmap = NULL; if (hSrcBitmap) // create a DIB from caller's bitmap (Clipper test program) { //---- reuse our bitmap ---- hTempBitmap = g_pBitmapCacheScaled->AcquireBitmap(hdc, iDestWidth, iDestHeight); if (hTempBitmap) { HDC hdcDest = CreateCompatibleDC(hdc); if (hdcDest) { HBITMAP hOldDestBitmap = (HBITMAP)SelectObject(hdcDest, hTempBitmap); HDC hdcSrc = CreateCompatibleDC(hdc); if (hdcSrc) { SetLayout(hdcSrc, 0); SetLayout(hdcDest, 0); HBITMAP hOldSrcBitmap = (HBITMAP) SelectObject(hdcSrc, hSrcBitmap); int iOldSM = SetStretchBltMode(hdcDest, COLORONCOLOR); //---- stretch src to dest ---- StretchBlt(hdcDest, 0, 0, iDestWidth, iDestHeight, hdcSrc, ixSrcOffset, iySrcOffset, iSrcWidth, iSrcHeight, SRCCOPY); SetStretchBltMode(hdcDest, iOldSM); SelectObject(hdcSrc, hOldSrcBitmap); DeleteDC(hdcSrc); } SelectObject(hdcDest, hOldDestBitmap); DeleteDC(hdcDest); } } } return hTempBitmap; } //--------------------------------------------------------------------------- HBITMAP CreateUnscaledTempBitmap(HDC hdc, HBITMAP hSrcBitmap, int ixSrcOffset, int iySrcOffset, int iDestWidth, int iDestHeight) { HBITMAP hTempBitmap = NULL; if (hSrcBitmap) // create a DIB from caller's bitmap (Clipper test program) { //---- reuse our bitmap ---- hTempBitmap = g_pBitmapCacheUnscaled->AcquireBitmap(hdc, iDestWidth, iDestHeight); if (hTempBitmap) { HDC hdcDest = CreateCompatibleDC(hdc); if (hdcDest) { HBITMAP hOldDestBitmap = (HBITMAP) SelectObject(hdcDest, hTempBitmap); HDC hdcSrc = CreateCompatibleDC(hdc); if (hdcSrc) { SetLayout(hdcSrc, 0); SetLayout(hdcDest, 0); HBITMAP hOldSrcBitmap = (HBITMAP) SelectObject(hdcSrc, hSrcBitmap); //---- copy src to dest ---- BitBlt(hdcDest, 0, 0, iDestWidth, iDestHeight, hdcSrc, ixSrcOffset, iySrcOffset, SRCCOPY); SelectObject(hdcSrc, hOldSrcBitmap); DeleteDC(hdcSrc); } SelectObject(hdcDest, hOldDestBitmap); DeleteDC(hdcDest); } } } return hTempBitmap; } //--------------------------------------------------------------------------- HRESULT CImageFile::DrawBackgroundDS(DIBINFO *pdi, TMBITMAPHEADER *pThemeBitmapHeader, BOOL fStock, CRenderObj *pRender, HDC hdc, int iStateId, const RECT *pRect, BOOL fForceStretch, MARGINS *pmarDest, float xMarginFactor, float yMarginFactor, OPTIONAL const DTBGOPTS *pOptions) { //---- bitmaps we may create ---- HBITMAP hBitmapStock = NULL; HBITMAP hBitmapTempScaled = NULL; HBITMAP hBitmapTempUnscaled = NULL; //---- copy of bitmap handle to use ---- HBITMAP hDsBitmap = NULL; HRESULT hr = S_OK; int iTempSrcWidth = pdi->iSingleWidth; int iTempSrcHeight = pdi->iSingleHeight; int iXOffset, iYOffset; GetOffsets(iStateId, pdi, &iXOffset, &iYOffset); if (pThemeBitmapHeader) // get stock bitmap (32 bit format) { hr = pRender->GetBitmap(hdc, pdi->iDibOffset, &hBitmapStock); if (FAILED(hr)) goto exit; hDsBitmap = hBitmapStock; } else // caller passed in bitmap (unknown format) { hBitmapTempUnscaled = CreateUnscaledTempBitmap(hdc, pdi->hProcessBitmap, iXOffset, iYOffset, pdi->iSingleWidth, pdi->iSingleHeight); if (! hBitmapTempUnscaled) { hr = E_FAIL; goto exit; } hDsBitmap = hBitmapTempUnscaled; //---- src is now just a single image ---- iXOffset = iYOffset = 0; } //---- handle scaled margins ---- if ((xMarginFactor != 1) || (yMarginFactor != 1)) { iTempSrcWidth = int(pdi->iSingleWidth * xMarginFactor); iTempSrcHeight = int(pdi->iSingleHeight * yMarginFactor); hBitmapTempScaled = CreateScaledTempBitmap(hdc, hDsBitmap, iXOffset, iYOffset, pdi->iSingleWidth, pdi->iSingleHeight, iTempSrcWidth, iTempSrcHeight); if (! hBitmapTempScaled) { hr = E_FAIL; goto exit; } hDsBitmap = hBitmapTempScaled; //---- src is now just a single image ---- iXOffset = iYOffset = 0; } if (hDsBitmap) { RECTL rclSrc = { iXOffset, iYOffset, iXOffset + iTempSrcWidth, iYOffset + iTempSrcHeight }; RECTL rclDest = { pRect->left, pRect->top, pRect->right, pRect->bottom }; // Flip Dest Rect if someone passed us inverted co-ordinates if (rclDest.left > rclDest.right) { int xTemp = rclDest.left; rclDest.left = rclDest.right; rclDest.right = xTemp; } if (rclDest.top > rclDest.bottom) { int yTemp = rclDest.bottom; rclDest.bottom = rclDest.top; rclDest.top = yTemp; } DWORD dwOptionFlags = 0; if (pOptions) { dwOptionFlags = pOptions->dwFlags; } // Initialize Drawing Stream BYTE stream[500]; BYTE* pvStreamStart = stream; BYTE* pvStream = stream; RECTL rclClip = rclDest; if (dwOptionFlags & DTBG_CLIPRECT) { IntersectRect((LPRECT)&rclClip, (LPRECT)&rclDest, &pOptions->rcClip); } StreamInit(&pvStream, hdc, hDsBitmap, &rclClip); DS_NINEGRID* pvNineGrid = (DS_NINEGRID*)pvStream; pvNineGrid->ulCmdID = DS_NINEGRIDID; if ((fForceStretch) || (pdi->eSizingType == ST_STRETCH)) { pvNineGrid->ngi.flFlags = DSDNG_STRETCH; } else if (pdi->eSizingType == ST_TRUESIZE) { pvNineGrid->ngi.flFlags = DSDNG_TRUESIZE; } else { pvNineGrid->ngi.flFlags = DSDNG_TILE; } if (pdi->fAlphaChannel) { pvNineGrid->ngi.flFlags |= DSDNG_PERPIXELALPHA; } else if (pdi->fTransparent) { pvNineGrid->ngi.flFlags |= DSDNG_TRANSPARENT; } if ((dwOptionFlags & DTBG_MIRRORDC) || (IsMirrored(hdc))) { if (_fMirrorImage) { pvNineGrid->ngi.flFlags |= DSDNG_MUSTFLIP; //---- workaround: needed by GdiDrawStream if we don't have a mirrored DC ---- //---- gdi should only look at the DSDNG_MUSTFLIP flag ---- if (! IsMirrored(hdc)) { int xTemp = rclDest.left; rclDest.left = rclDest.right; rclDest.right = xTemp; } } } pvNineGrid->rclDst = rclDest; pvNineGrid->rclSrc = rclSrc; if (pdi->eSizingType == ST_TRUESIZE) { pvNineGrid->ngi.ulLeftWidth = 0; pvNineGrid->ngi.ulRightWidth = 0; pvNineGrid->ngi.ulTopHeight = 0; pvNineGrid->ngi.ulBottomHeight = 0; } else { //---- copy scaled Src margins ---- pvNineGrid->ngi.ulLeftWidth = pmarDest->cxLeftWidth; pvNineGrid->ngi.ulRightWidth = pmarDest->cxRightWidth; pvNineGrid->ngi.ulTopHeight = pmarDest->cyTopHeight; pvNineGrid->ngi.ulBottomHeight = pmarDest->cyBottomHeight; } pvNineGrid->ngi.crTransparent = pdi->crTransparent; pvStream += sizeof(DS_NINEGRID); GdiDrawStream(hdc, (int)(pvStream - pvStreamStart), (char*) pvStreamStart); } else { hr = E_FAIL; } exit: //---- clean up temp bitmaps ---- if (hBitmapTempScaled) { g_pBitmapCacheScaled->ReturnBitmap(); } if (hBitmapTempUnscaled) { g_pBitmapCacheUnscaled->ReturnBitmap(); } if ((hBitmapStock) && (! fStock)) // not really stock (was "create on demand") { pRender->ReturnBitmap(hBitmapStock); } return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::DrawBackgroundDNG(DIBINFO *pdi, TMBITMAPHEADER *pThemeBitmapHeader, BOOL fStock, CRenderObj *pRender, HDC hdc, int iStateId, const RECT *pRect, BOOL fForceStretch, MARGINS *pmarDest, OPTIONAL const DTBGOPTS *pOptions) { HRESULT hr = E_FAIL; //---- options ---- DWORD dwOptionFlags = 0; const RECT *pClipRect = NULL; if (pOptions) { dwOptionFlags = pOptions->dwFlags; if (dwOptionFlags & DTBG_CLIPRECT) pClipRect = &pOptions->rcClip; } int iXOffset, iYOffset; GetOffsets(iStateId, pdi, &iXOffset, &iYOffset); DWORD dwFlags = 0; if (! (dwOptionFlags & DTBG_DRAWSOLID)) { if (pdi->fTransparent) { dwFlags = NGI_TRANS; } if (pdi->fAlphaChannel) { dwFlags = NGI_ALPHA; } } ULONG* pvSrcBits = NULL; int iWidth = 0; int iHeight = 0; BOOL fFreeBits = FALSE; if (pThemeBitmapHeader) { BITMAPINFOHEADER* pHeader = BITMAPDATA(pThemeBitmapHeader); if (pHeader && pHeader->biBitCount == 32) { pvSrcBits = (ULONG*)DIBDATA(pHeader); } iWidth = pHeader->biWidth; iHeight = pHeader->biHeight; } else if (pdi->hProcessBitmap) { BITMAP bm; if (GetObject(pdi->hProcessBitmap, sizeof(bm), &bm)) { BITMAPINFO bmInfo = {{sizeof(BITMAPINFOHEADER), bm.bmWidth, bm.bmHeight, 1, 32, BI_RGB, 0, 0, 0, 0, 0}, NULL}; pvSrcBits = new DWORD[bm.bmWidth * bm.bmHeight]; if (pvSrcBits) { if (GetDIBits(hdc, pdi->hProcessBitmap, 0, bm.bmHeight, pvSrcBits, &bmInfo, DIB_RGB_COLORS)) { fFreeBits = TRUE; iWidth = bm.bmWidth; iHeight = bm.bmHeight; } else { delete[] pvSrcBits; pvSrcBits = NULL; } } } } if (pvSrcBits) { NGIMAGE ngi; ngi.hbm = NULL; ngi.iWidth = pdi->iSingleWidth; ngi.iHeight = pdi->iSingleHeight; ngi.margin = _SizingMargins; //ngi.marDest = *pmarDest; ngi.dwFlags = dwFlags; ngi.crTrans = pdi->crTransparent; if (fForceStretch) ngi.eSize = ST_STRETCH; else ngi.eSize = pdi->eSizingType; DWORD dwDNGFlags = 0; if ((dwOptionFlags & DTBG_MIRRORDC) || (IsMirrored(hdc))) { if (_fMirrorImage) { dwDNGFlags |= DNG_MUSTFLIP; } } ngi.iBufWidth = iWidth; int iDibOffset = (iHeight - iYOffset) - pdi->iSingleHeight; ngi.pvBits = pvSrcBits + (iDibOffset * ngi.iBufWidth) + iXOffset; RECT rcDest = *pRect; if (pdi->fBorderOnly && (RECTHEIGHT(&rcDest) > ngi.margin.cyTopHeight + ngi.margin.cyBottomHeight) && (RECTWIDTH(&rcDest) > ngi.margin.cxLeftWidth + ngi.margin.cxRightWidth)) { RECT rcTop = rcDest; RECT rcLeft = rcDest; RECT rcBottom = rcDest; RECT rcRight = rcDest; rcLeft.top = rcRight.top = rcTop.bottom = rcTop.top + ngi.margin.cyTopHeight; rcLeft.bottom = rcRight.bottom = rcBottom.top = rcBottom.bottom - ngi.margin.cyBottomHeight; rcLeft.right = rcLeft.left + ngi.margin.cxLeftWidth; rcRight.left = rcRight.right - ngi.margin.cxRightWidth; if (pClipRect) { IntersectRect(&rcLeft, pClipRect, &rcLeft); IntersectRect(&rcTop, pClipRect, &rcTop); IntersectRect(&rcRight, pClipRect, &rcRight); IntersectRect(&rcBottom, pClipRect, &rcBottom); } hr = DrawNineGrid2(hdc, &ngi, &rcDest, &rcTop, dwDNGFlags); if (SUCCEEDED(hr)) { hr = DrawNineGrid2(hdc, &ngi, &rcDest, &rcLeft, dwDNGFlags); if (SUCCEEDED(hr)) { hr = DrawNineGrid2(hdc, &ngi, &rcDest, &rcRight, dwDNGFlags); if (SUCCEEDED(hr)) { hr = DrawNineGrid2(hdc, &ngi, &rcDest, &rcBottom, dwDNGFlags); } } } } else { hr = DrawNineGrid2(hdc, &ngi, &rcDest, pClipRect, dwDNGFlags); } if (fFreeBits) { delete[] pvSrcBits; } } if (FAILED(hr)) { Log(LOG_ALWAYS, L"DrawBackground FAILED: class=%s, hr=0x%x", SHARECLASS(pRender), hr); } return hr; } //--------------------------------------------------------------------------- DIBINFO *CImageFile::SelectCorrectImageFile(CRenderObj *pRender, HDC hdc, OPTIONAL const RECT *prc, BOOL fForGlyph, OPTIONAL TRUESTRETCHINFO *ptsInfo) { DIBINFO *pdiDefault = (fForGlyph) ? &_GlyphInfo : &_ImageInfo; DIBINFO *pdi = NULL; BOOL fForceRectSizing = FALSE; int iWidth = 1; int iHeight = 1; //---- do we need a screen dc? ---- BOOL fReleaseDC = FALSE; if (! hdc) { hdc = GetWindowDC(NULL); if (hdc) fReleaseDC = TRUE; } if (prc) { iWidth = WIDTH(*prc); iHeight = HEIGHT(*prc); } //---- see if our clients wants to force a TRUESIZE to stretch ---- if ((fForGlyph) || (_ImageInfo.eSizingType == ST_TRUESIZE)) { if ((pRender) && (pRender->_dwOtdFlags & OTD_FORCE_RECT_SIZING)) { fForceRectSizing = TRUE; } } //---- find correct file by DPI or Size ---- if ((fForGlyph) || (_eGlyphType != GT_IMAGEGLYPH)) // match multifiles to reg or glyph { BOOL fSizing = FALSE; BOOL fDpi = FALSE; if ((fForceRectSizing) || (_eImageSelectType == IST_SIZE) || (_fSourceGrow)) { if (prc) fSizing = TRUE; } else { fDpi = (_eImageSelectType == IST_DPI); } if (fDpi) // DPI-based image selection { int iMinDestDpi = __min(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); //---- search from largest to smallest ---- for (int i=_iMultiImageCount-1; i >= 0; i--) { if (MultiDibPtr(i)->iMinDpi <= iMinDestDpi) // got him { pdi = MultiDibPtr(i); break; } } } else if (fSizing) // Sizing-base image selection { if (_iMultiImageCount) { //---- search from largest to smallest ---- for (int i=_iMultiImageCount-1; i >= 0; i--) { DIBINFO *pdii = MultiDibPtr(i); if ((pdii->szMinSize.cx <= iWidth) && (pdii->szMinSize.cy <= iHeight)) { pdi = pdii; break; } } } } } if (! pdi) // no match found { pdi = pdiDefault; } //---- determine drawing size of selected file (MultiImage or regular) ---- if (ptsInfo) { ptsInfo->fForceStretch = FALSE; ptsInfo->fFullStretch = FALSE; ptsInfo->szDrawSize.cx = 0; ptsInfo->szDrawSize.cy = 0; //---- this sizing only applies to TRUESIZE images ---- if ((pdi->eSizingType == ST_TRUESIZE) && (_eTrueSizeScalingType != TSST_NONE)) { if (prc) { //---- force an exact stretch match? ---- if ((fForceRectSizing) || (pdi->iSingleWidth > iWidth) || (pdi->iSingleHeight > iHeight)) { //---- either Forced to stretch by caller or image is too big for dest RECT ---- ptsInfo->fForceStretch = TRUE; ptsInfo->fFullStretch = TRUE; ptsInfo->szDrawSize.cx = iWidth; ptsInfo->szDrawSize.cy = iHeight; } } if (! ptsInfo->fForceStretch) // keep trying.. { //---- see if image is too small for dest RECT --- SIZE szTargetSize = {0, 0}; if (_eTrueSizeScalingType == TSST_DPI) { int ixDpiDc = GetDeviceCaps(hdc, LOGPIXELSX); int iyDpiDc = GetDeviceCaps(hdc, LOGPIXELSY); szTargetSize.cx = MulDiv(pdi->iSingleWidth, ixDpiDc, pdi->iMinDpi); szTargetSize.cy = MulDiv(pdi->iSingleHeight, iyDpiDc, pdi->iMinDpi); } else if ((_eTrueSizeScalingType == TSST_SIZE) && (prc)) { szTargetSize.cx = MulDiv(pdi->iSingleWidth, iWidth, pdi->szMinSize.cx); szTargetSize.cy = MulDiv(pdi->iSingleHeight, iHeight, pdi->szMinSize.cy); } if (szTargetSize.cx) // was set { //---- clip targetsize against dest rect ---- if (prc) { szTargetSize.cx = __min(szTargetSize.cx, iWidth); szTargetSize.cy = __min(szTargetSize.cy, iHeight); } int ixPercentExceed = 100*(szTargetSize.cx - pdi->iSingleWidth)/pdi->iSingleWidth; int iyPercentExceed = 100*(szTargetSize.cy - pdi->iSingleHeight)/pdi->iSingleHeight; if ((ixPercentExceed >= _iTrueSizeStretchMark) && (iyPercentExceed >= _iTrueSizeStretchMark)) { ptsInfo->fForceStretch = TRUE; ptsInfo->szDrawSize = szTargetSize; } } } } } if (! pdi) { pdi = pdiDefault; } if (fReleaseDC) { ReleaseDC(NULL, hdc); } return pdi; } //--------------------------------------------------------------------------- void CImageFile::GetDrawnImageSize(DIBINFO *pdi, const RECT *pRect, TRUESTRETCHINFO *ptsInfo, SIZE *pszDraw) { //---- szDraw is the size image will be drawn to ---- if (pdi->eSizingType == ST_TRUESIZE) { if (ptsInfo->fForceStretch) { *pszDraw = ptsInfo->szDrawSize; //---- integral sizing (stretched truesize only) ---- if ((_fIntegralSizing) && (! ptsInfo->fFullStretch)) { float flFactX = float(ptsInfo->szDrawSize.cx)/pdi->iSingleWidth; float flFactY = float(ptsInfo->szDrawSize.cy)/pdi->iSingleHeight; //---- cast float's to int to get lowest int (vs. rounded) ---- pszDraw->cx = pdi->iSingleWidth * int(flFactX); pszDraw->cy = pdi->iSingleHeight * int(flFactY); } } else // use original image size { pszDraw->cx = pdi->iSingleWidth; pszDraw->cy = pdi->iSingleHeight; } //---- Uniform Sizing ---- if (_fUniformSizing) { int iSingleWidth = pdi->iSingleWidth; int iSingleHeight = pdi->iSingleHeight; double fact1 = double(pszDraw->cx)/iSingleWidth; double fact2 = double(pszDraw->cy)/iSingleHeight; //---- select the smallest factor to use for both dims ---- if (fact1 < fact2) { pszDraw->cy = int(iSingleHeight*fact1); } else if (fact1 > fact2) { pszDraw->cx = int(iSingleWidth*fact2); } } } else // ST_TILE or ST_STRETCH: pRect determines size { if (pRect) { pszDraw->cx = WIDTH(*pRect); pszDraw->cy = HEIGHT(*pRect); } else // void function so just return 0 { pszDraw->cx = 0; pszDraw->cy = 0; } } } //--------------------------------------------------------------------------- HRESULT CImageFile::DrawImageInfo(DIBINFO *pdi, CRenderObj *pRender, HDC hdc, int iStateId, const RECT *pRect, const DTBGOPTS *pOptions, TRUESTRETCHINFO *ptsInfo) { HRESULT hr = S_OK; TMBITMAPHEADER *pThemeBitmapHeader = NULL; BOOL fStock = FALSE; RECT rcLocal; DWORD dwFlags; SIZE szDraw; BOOL fRectFilled; MARGINS marDest; float xFactor; float yFactor; if (pOptions) dwFlags = pOptions->dwFlags; else dwFlags = 0; //---- validate bitmap header ---- if (! pdi->hProcessBitmap) // regular, section based DIB { if (pRender->_pbThemeData && pdi->iDibOffset > 0) { pThemeBitmapHeader = reinterpret_cast(pRender->_pbThemeData + pdi->iDibOffset); ASSERT(pThemeBitmapHeader->dwSize == TMBITMAPSIZE); fStock = (pThemeBitmapHeader->hBitmap != NULL); } if (!pRender->IsReady()) { // Stock bitmaps in section are cleaning, don't try to paint with an old HBITMAP hr = E_FAIL; //Log(LOG_TMBITMAP, L"Obsolete theme section: class=%s", SHARECLASS(pRender)); goto exit; } if (pThemeBitmapHeader == NULL) { hr = E_FAIL; Log(LOG_ALWAYS, L"No TMBITMAPHEADER: class=%s, hr=0x%x", SHARECLASS(pRender), hr); goto exit; } } //----- set szDraw to size image will be drawn at ---- GetDrawnImageSize(pdi, pRect, ptsInfo, &szDraw); rcLocal = *pRect; fRectFilled = TRUE; //---- horizontal alignment ---- if (WIDTH(rcLocal) > szDraw.cx) { fRectFilled = FALSE; if (_eHAlign == HA_LEFT) { rcLocal.right = rcLocal.left + szDraw.cx; } else if (_eHAlign == HA_CENTER) { rcLocal.left += (WIDTH(rcLocal) - szDraw.cx) / 2; rcLocal.right = rcLocal.left + szDraw.cx; } else { rcLocal.left = rcLocal.right - szDraw.cx; } } //---- vertical alignment ---- if (HEIGHT(rcLocal) > szDraw.cy) { fRectFilled = FALSE; if (_eVAlign == VA_TOP) { rcLocal.bottom = rcLocal.top + szDraw.cy; } else if (_eVAlign == VA_CENTER) { rcLocal.top += (HEIGHT(rcLocal) - szDraw.cy) / 2; rcLocal.bottom = rcLocal.top + szDraw.cy; } else { rcLocal.top = rcLocal.bottom - szDraw.cy; } } //---- BgFill ---- if ((! fRectFilled) && (! pdi->fBorderOnly) && (_fBgFill)) { if (! (dwFlags & DTBG_OMITCONTENT)) { //---- paint bg ---- HBRUSH hbr = CreateSolidBrush(_crFill); if (! hbr) { hr = GetLastError(); goto exit; } FillRect(hdc, pRect, hbr); DeleteObject(hbr); } } //---- calculate source/margin scaling factors ---- marDest = _SizingMargins; if (pdi->eSizingType == ST_TRUESIZE) // sizing margins ignored - no scaling needed { xFactor = 1; yFactor = 1; } else { //---- scale destination sizing margins ---- ScaleMargins(&marDest, hdc, pRender, pdi, &szDraw, &xFactor, &yFactor); } #if 1 // keep this in sync with #if in parser.cpp //---- new GDI drawing ---- hr = DrawBackgroundDS(pdi, pThemeBitmapHeader, fStock, pRender, hdc, iStateId, &rcLocal, ptsInfo->fForceStretch, &marDest, xFactor, yFactor, pOptions); #else //---- old drawing (keep around until DS is burned in) ---- hr = DrawBackgroundDNG(pdi, pThemeBitmapHeader, fStock, pRender, hdc, iStateId, &rcLocal, ptsInfo->fForceStretch, &marDest, pOptions); #endif exit: return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::DrawBackground(CRenderObj *pRender, HDC hdc, int iStateId, const RECT *pRect, OPTIONAL const DTBGOPTS *pOptions) { HRESULT hr = S_OK; TRUESTRETCHINFO tsInfo; if (! _fGlyphOnly) { DIBINFO *pdi = SelectCorrectImageFile(pRender, hdc, pRect, FALSE, &tsInfo); //---- draw normal image ---- hr = DrawImageInfo(pdi, pRender, hdc, iStateId, pRect, pOptions, &tsInfo); } //---- draw glyph, if needed ---- if (SUCCEEDED(hr) && (_eGlyphType != GT_NONE)) { RECT rc; hr = GetBackgroundContentRect(pRender, hdc, pRect, &rc); if (SUCCEEDED(hr)) { if (_eGlyphType == GT_FONTGLYPH) { hr = DrawFontGlyph(pRender, hdc, &rc, pOptions); } else { DIBINFO *pdi = SelectCorrectImageFile(pRender, hdc, &rc, TRUE, &tsInfo); //---- draw glyph image ---- hr = DrawImageInfo(pdi, pRender, hdc, iStateId, &rc, pOptions, &tsInfo); } } } return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::DrawFontGlyph(CRenderObj *pRender, HDC hdc, RECT *prc, OPTIONAL const DTBGOPTS *pOptions) { HRESULT hr = S_OK; DWORD dwFlags = DT_SINGLELINE; HFONT hFont = NULL; HFONT hOldFont = NULL; COLORREF crOld = 0; CSaveClipRegion scrOrig; int iOldMode = 0; WCHAR szText[2] = { (WCHAR)_iGlyphIndex, 0 }; //---- options ---- DWORD dwOptionFlags = 0; const RECT *pClipRect = NULL; if (pOptions) { dwOptionFlags = pOptions->dwFlags; if (dwOptionFlags & DTBG_CLIPRECT) pClipRect = &pOptions->rcClip; } //---- create the font ---- hr = pRender->GetScaledFontHandle(hdc, &_lfGlyphFont, &hFont); if (FAILED(hr)) goto exit; //---- make it active ---- hOldFont = (HFONT)SelectObject(hdc, hFont); if (! hOldFont) { hr = MakeErrorLast(); goto exit; } //---- set the text color ---- crOld = SetTextColor(hdc, _crGlyphTextColor); //---- draw text with transparent background ---- iOldMode = SetBkMode(hdc, TRANSPARENT); //---- set the HORZ alignment flags ---- if (_eHAlign == HA_LEFT) dwFlags |= DT_LEFT; else if (_eHAlign == HA_CENTER) dwFlags |= DT_CENTER; else dwFlags |= DT_RIGHT; //---- set the VERT alignment flags ---- if (_eVAlign == VA_TOP) dwFlags |= DT_TOP; else if (_eVAlign == VA_CENTER) dwFlags |= DT_VCENTER; else dwFlags |= DT_BOTTOM; //---- add clipping ---- if (pClipRect) { //---- get previous clipping region (for restoring at end) ---- hr = scrOrig.Save(hdc); if (FAILED(hr)) goto exit; //---- add "pClipRect" to the GDI clipping region ---- int iRetVal = IntersectClipRect(hdc, pClipRect->left, pClipRect->top, pClipRect->right, pClipRect->bottom); if (iRetVal == ERROR) { hr = MakeErrorLast(); goto exit; } } //---- draw the char ---- if (! DrawTextEx(hdc, szText, 1, prc, dwFlags, NULL)) { hr = MakeErrorLast(); goto exit; } exit: if (pClipRect) scrOrig.Restore(hdc); //---- reset the background mode ---- if (iOldMode != TRANSPARENT) SetBkMode(hdc, iOldMode); //---- restore text color ---- if (crOld != _crGlyphTextColor) SetTextColor(hdc, crOld); //---- restore font ---- if (hOldFont) SelectObject(hdc, hOldFont); if (hFont) pRender->ReturnFontHandle(hFont); return hr; } //--------------------------------------------------------------------------- BOOL CImageFile::IsBackgroundPartiallyTransparent(int iStateId) { DIBINFO *pdi = &_ImageInfo; // primary image determines transparency return ((pdi->fAlphaChannel) || (pdi->fTransparent)); } //--------------------------------------------------------------------------- HRESULT CImageFile::HitTestBackground(CRenderObj *pRender, OPTIONAL HDC hdc, int iStateId, DWORD dwHTFlags, const RECT *pRect, HRGN hrgn, POINT ptTest, OUT WORD *pwHitCode) { *pwHitCode = HTNOWHERE; if (! PtInRect(pRect, ptTest)) return S_OK; // nowhere //---- background might have transparent parts - get its region ---- HRESULT hr = S_OK; HRGN hrgnBk = NULL; if( !hrgn && IsBackgroundPartiallyTransparent(iStateId) ) { hr = GetBackgroundRegion(pRender, hdc, iStateId, pRect, &hrgnBk); if( SUCCEEDED(hr) ) hrgn = hrgnBk; } MARGINS margins; if( TESTFLAG(dwHTFlags, HTTB_SYSTEMSIZINGMARGINS) && TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER) && !TESTFLAG(dwHTFlags, HTTB_SIZINGTEMPLATE) ) { ZeroMemory( &margins, sizeof(margins) ); int cxBorder = ClassicGetSystemMetrics( SM_CXSIZEFRAME ); int cyBorder = ClassicGetSystemMetrics( SM_CXSIZEFRAME ); if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_LEFT) ) margins.cxLeftWidth = cxBorder; if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_RIGHT) ) margins.cxRightWidth = cxBorder; if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_TOP) ) margins.cyTopHeight = cyBorder; if( TESTFLAG(dwHTFlags, HTTB_RESIZINGBORDER_BOTTOM) ) margins.cyBottomHeight = cyBorder; } else { hr = GetScaledContentMargins(pRender, hdc, pRect, &margins); if (FAILED(hr)) goto exit; } if( hrgn ) { // 122013 - we originally delegated to a sophisticated but broken // resizing area region hit test algorithm for regioned windows, // but for whistler we'll just do the bounding // rectangle thang instead. //*pwHitCode = HitTestRgn( dwHTFlags, pRect, hrgn, margins, ptTest ); RECT rcRgn; if( GetRgnBox( hrgn, &rcRgn ) ) { if( TESTFLAG(dwHTFlags, HTTB_SIZINGTEMPLATE) ) { *pwHitCode = HitTestTemplate( dwHTFlags, &rcRgn, hrgn, margins, ptTest ); } else { *pwHitCode = HitTestRect( dwHTFlags, &rcRgn, margins, ptTest ); } } SAFE_DELETE_GDIOBJ(hrgnBk); } else { *pwHitCode = HitTestRect( dwHTFlags, pRect, margins, ptTest ); } exit: return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::GetBackgroundRegion(CRenderObj *pRender, OPTIONAL HDC hdc, int iStateId, const RECT *pRect, HRGN *pRegion) { HRESULT hr = S_OK; RGNDATA *pRgnData; CMemoryDC hdcMemory; HRGN hrgn; int iRgnDataOffset = 0; MIXEDPTRS u; DIBINFO *pdi = SelectCorrectImageFile(pRender, hdc, pRect, FALSE); //---- get rgndata offset ---- if ((pdi->iRgnListOffset) && (pRender->_pbThemeData)) { u.pb = pRender->_pbThemeData + pdi->iRgnListOffset; int iMaxState = (*u.pb++) - 1; if (iStateId > iMaxState) iStateId = 0; iRgnDataOffset = u.pi[iStateId]; } //---- see if it even has a transparent part ---- if (iRgnDataOffset) { //---- stretch those puppies & create a new region ---- pRgnData = (RGNDATA *)(pRender->_pbThemeData + iRgnDataOffset + sizeof(RGNDATAHDR) + ENTRYHDR_SIZE); SIZE szSrcImage = {pdi->iSingleWidth, pdi->iSingleHeight}; hr = _ScaleRectsAndCreateRegion(pRgnData, pRect, &_SizingMargins, &szSrcImage, &hrgn); if (FAILED(hr)) goto exit; } else { //---- return the bounding rect as the region ---- hrgn = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom); if (! hrgn) { hr = MakeErrorLast(); goto exit; } } *pRegion = hrgn; exit: return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::GetBackgroundContentRect(CRenderObj *pRender, OPTIONAL HDC hdc, const RECT *pBoundingRect, RECT *pContentRect) { MARGINS margins; HRESULT hr = GetScaledContentMargins(pRender, hdc, pBoundingRect, &margins); if (FAILED(hr)) goto exit; pContentRect->left = pBoundingRect->left + margins.cxLeftWidth; pContentRect->top = pBoundingRect->top + margins.cyTopHeight; pContentRect->right = pBoundingRect->right - margins.cxRightWidth; pContentRect->bottom = pBoundingRect->bottom - margins.cyBottomHeight; exit: return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::GetBackgroundExtent(CRenderObj *pRender, OPTIONAL HDC hdc, const RECT *pContentRect, RECT *pExtentRect) { MARGINS margins; HRESULT hr = GetScaledContentMargins(pRender, hdc, pContentRect, &margins); if (FAILED(hr)) goto exit; pExtentRect->left = pContentRect->left - margins.cxLeftWidth; pExtentRect->top = pContentRect->top-+ margins.cyTopHeight; pExtentRect->right = pContentRect->right + margins.cxRightWidth; pExtentRect->bottom = pContentRect->bottom + margins.cyBottomHeight; exit: return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::GetScaledContentMargins(CRenderObj *pRender, OPTIONAL HDC hdc, OPTIONAL const RECT *prcDest, MARGINS *pMargins) { HRESULT hr = S_OK; *pMargins = _ContentMargins; //---- now scale the margins ---- SIZE szDraw; TRUESTRETCHINFO tsInfo; DIBINFO *pdi = SelectCorrectImageFile(pRender, hdc, prcDest, FALSE, NULL); GetDrawnImageSize(pdi, prcDest, &tsInfo, &szDraw); hr = ScaleMargins(pMargins, hdc, pRender, pdi, &szDraw); return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::GetPartSize(CRenderObj *pRender, HDC hdc, OPTIONAL const RECT *prc, THEMESIZE eSize, SIZE *psz) { HRESULT hr = S_OK; TRUESTRETCHINFO tsInfo; DIBINFO *pdi = SelectCorrectImageFile(pRender, hdc, prc, FALSE, &tsInfo); if (eSize == TS_MIN) { MARGINS margins; hr = GetScaledContentMargins(pRender, hdc, prc, &margins); if (FAILED(hr)) goto exit; psz->cx = max(1, margins.cxLeftWidth + margins.cxRightWidth); psz->cy = max(1, margins.cyTopHeight + margins.cyBottomHeight); } else if (eSize == TS_TRUE) { psz->cx = pdi->iSingleWidth; psz->cy = pdi->iSingleHeight; } else if (eSize == TS_DRAW) { GetDrawnImageSize(pdi, prc, &tsInfo, psz); } else { hr = MakeError32(E_INVALIDARG); goto exit; } exit: return hr; } //--------------------------------------------------------------------------- HRESULT CImageFile::GetBitmap(CRenderObj *pRender, HDC hdc, const RECT *prc, HBITMAP *phBitmap) { int iStockDibOffset = pRender->GetValueIndex(_iSourcePartId, _iSourceStateId, TMT_STOCKDIBDATA); if (iStockDibOffset > 0) { return pRender->GetBitmap(NULL, iStockDibOffset, phBitmap); } else { return E_INVALIDARG; } } //--------------------------------------------------------------------------- void CImageFile::GetOffsets(int iStateId, DIBINFO *pdi, int *piXOffset, int *piYOffset) { if (_eImageLayout == IL_HORIZONTAL) { //---- iStateId in the image index ---- if ((iStateId <= 0) || (iStateId > _iImageCount)) *piXOffset = 0; else *piXOffset = (iStateId-1) * (pdi->iSingleWidth); *piYOffset = 0; } else // vertical { //---- iStateId in the image index ---- if ((iStateId <= 0) || (iStateId > _iImageCount)) *piYOffset = 0; else *piYOffset = (iStateId-1) * (pdi->iSingleHeight); *piXOffset = 0; } } //--------------------------------------------------------------------------- HRESULT CImageFile::ScaleMargins(IN OUT MARGINS *pMargins, HDC hdcOrig, CRenderObj *pRender, DIBINFO *pdi, const SIZE *pszDraw, OPTIONAL float *pfx, OPTIONAL float *pfy) { HRESULT hr = S_OK; COptionalDC hdc(hdcOrig); BOOL fForceRectSizing = FALSE; if ((pRender) && (pRender->_dwOtdFlags & OTD_FORCE_RECT_SIZING)) { fForceRectSizing = TRUE; } float xFactor = 1; float yFactor = 1; //---- any margins to size? ---- if ((pMargins->cxLeftWidth) || (pMargins->cxRightWidth) || (pMargins->cyBottomHeight) || (pMargins->cyTopHeight)) { if ((pszDraw->cx > 0) && (pszDraw->cy > 0)) { BOOL fxNeedScale = FALSE; BOOL fyNeedScale = FALSE; //---- scale if dest rect is too small in one dimension ---- if ((_fSourceShrink) || (fForceRectSizing)) { if (pszDraw->cx < pdi->szMinSize.cx) { fxNeedScale = TRUE; } if (pszDraw->cy < pdi->szMinSize.cy) { fyNeedScale = TRUE; } } if ((_fSourceGrow) || (fForceRectSizing)) { if ((! fxNeedScale) && (! fyNeedScale)) { //---- calculate our Dest DPI ---- int iDestDpi; if (fForceRectSizing) { iDestDpi = (pRender) ? (pRender->GetDpiOverride()) : 0; if (! iDestDpi) { //---- make up a DPI based on sizes (IE will pass us the actual DPI soon) ---- int ixFakeDpi = MulDiv(pdi->iMinDpi, pszDraw->cx, _szNormalSize.cx); int iyFakeDpi = MulDiv(pdi->iMinDpi, pszDraw->cy, _szNormalSize.cy); iDestDpi = (ixFakeDpi + iyFakeDpi)/2; } } else { iDestDpi = GetDeviceCaps(hdc, LOGPIXELSX); } //---- scale source/margins by Dest DPI ---- if (iDestDpi >= 2*pdi->iMinDpi) { xFactor *= iDestDpi/pdi->iMinDpi; yFactor *= iDestDpi/pdi->iMinDpi; } } } //---- scale by ratio of our image to draw size ---- if (fxNeedScale) { xFactor *= float(pszDraw->cx)/float(_szNormalSize.cx); } if (fyNeedScale) { yFactor *= float(pszDraw->cy)/float(_szNormalSize.cy); } } //---- use smallest factor for both ---- if (xFactor < yFactor) { yFactor = xFactor; } else if (yFactor < xFactor) { xFactor = yFactor; } //---- integer truncation ---- if (xFactor > 1.0) { xFactor = float(int(xFactor)); } if (yFactor > 1.0) { yFactor = float(int(yFactor)); } //---- scale the margin values ---- if (xFactor != 1) { pMargins->cxLeftWidth = ROUND(xFactor*pMargins->cxLeftWidth); pMargins->cxRightWidth = ROUND(xFactor*pMargins->cxRightWidth); } if (yFactor != 1) { pMargins->cyTopHeight = ROUND(yFactor*pMargins->cyTopHeight); pMargins->cyBottomHeight = ROUND(yFactor*pMargins->cyBottomHeight); } } //---- return factors to interested callers ---- if (pfx) { *pfx = xFactor; } if (pfy) { *pfy = yFactor; } return hr; } //---------------------------------------------------------------------------