// -------------------------------------------------------------------------- // Module Name: NineGrid2.cpp // // Copyright (c) 2000, 2001 Microsoft Corporation // // Implementation of the DrawNineGrid2 function // // History: 2000-12-20 justmann created // -------------------------------------------------------------------------- #include "stdafx.h" #include "resource.h" #include "tmschema.h" #include "ninegrid2.h" #define DNG_BUF_WIDTH 256 #define DNG_BUF_HEIGHT 60 typedef struct STRETCH { ULONG xStart; ULONG xAccum; ULONG xFrac; ULONG xInt; ULONG ulDestWidth; ULONG ulSrcWidth; int left; int right; } STRETCH; typedef struct DNGINTERNALDATAtag { int cxClipMin; int cxClipMax; ULONG* pvDestBits; int iDestWidth; int iClipWidth; ULONG* pvSrcBits; int iSrcWidth; int iSrcBufWidth; int cxLeftWidth; int xMinLeft; int xMaxLeft; int cxRightWidth; int xMinRight; int xMaxRight; int cxMiddleWidth; int cxNewMiddleWidth; int xMinMiddle; int xMaxMiddle; // Variable for shrunken corners and sides BOOL fShowMiddle; STRETCH stretchLeft; STRETCH stretchRight; int cxNewLeftWidth; int cxNewRightWidth; BOOL fTileMode; // Specific to non-tile mode (i.e. stretch mode) STRETCH stretchMiddle; } DNGINTERNALDATA; static HDC s_hdcBuf = NULL; static HBITMAP s_hbmBuf = NULL; static HBITMAP s_hbmOldBuf = NULL; static CRITICAL_SECTION s_dngLock; void DNG_FreeDIB(HDC* phdcDest, HBITMAP* phbmDest, HBITMAP* phbmDestOld); BOOL NineGrid2StartUp() { InitializeCriticalSection(&s_dngLock); return TRUE; } void NineGrid2ShutDown() { DNG_FreeDIB(&s_hdcBuf, &s_hbmBuf, &s_hbmOldBuf); DeleteCriticalSection(&s_dngLock); } inline void DNG_CreateDIB(HDC hdc, int iWidth, int iHeight, ULONG** ppvDestBits, HDC* phdcDest, HBITMAP* phbmDest, HBITMAP* phbmDestOld) { *phdcDest = CreateCompatibleDC(hdc); if (*phdcDest) { BITMAPINFO bi = {0}; bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = iWidth; bi.bmiHeader.biHeight = iHeight; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; *phbmDest = CreateDIBSection(*phdcDest, &bi, DIB_RGB_COLORS, (VOID**)ppvDestBits, NULL, 0); if (*phbmDest) { *phbmDestOld = (HBITMAP) SelectObject(*phdcDest, *phbmDest); } else { DeleteDC(*phdcDest); *phdcDest = NULL; } } } void DNG_FreeDIB(HDC* phdcDest, HBITMAP* phbmDest, HBITMAP* phbmDestOld) { if (*phdcDest) { SelectObject(*phdcDest, *phbmDestOld); DeleteObject(*phbmDest); DeleteDC(*phdcDest); } *phdcDest = NULL; } HRESULT BitmapToNGImage(HDC hdc, HBITMAP hbm, int left, int top, int right, int bottom, MARGINS margin, SIZINGTYPE eSize, DWORD dwFlags, COLORREF crTrans, PNGIMAGE pngi) { HRESULT hr = E_INVALIDARG; if (pngi) { pngi->margin = margin; pngi->eSize = eSize; pngi->dwFlags = dwFlags; pngi->crTrans = crTrans; pngi->iWidth = right - left; pngi->iHeight = bottom - top; HDC hdcBuf = NULL; HBITMAP hbmOld; DNG_CreateDIB(hdc, pngi->iWidth, pngi->iHeight, &(pngi->pvBits), &hdcBuf, &pngi->hbm, &hbmOld); if (hdcBuf) { HDC hdcTempMem = CreateCompatibleDC(hdc); if (hdcTempMem) { HBITMAP hbmTempOld = (HBITMAP) SelectObject(hdcTempMem, hbm); BitBlt(hdcBuf, 0, 0, pngi->iWidth, pngi->iHeight, hdcTempMem, left, top, SRCCOPY); hr = S_OK; SelectObject(hdcTempMem, hbmTempOld); DeleteDC(hdcTempMem); } SelectObject(hdcBuf, hbmOld); DeleteDC(hdcBuf); } } return hr; } HRESULT FreeNGImage(PNGIMAGE pngi) { HRESULT hr = E_INVALIDARG; if (pngi) { if (pngi->hbm) { DeleteObject(pngi->hbm); } pngi->hbm = NULL; hr = S_OK; } return hr; } inline void DNG_StretchRow(ULONG* pvDestBits, ULONG* pvSrcBits, STRETCH * ps) { ULONG* pvTemp = pvDestBits + ps->left; ULONG* pvSentinel = pvDestBits + ps->right; ULONG xInt = ps->xInt; ULONG xFrac = ps->xFrac; ULONG xTmp; ULONG xAccum = ps->xAccum; ULONG * pulSrc = pvSrcBits + ps->xStart; ULONG ulSrc; while (pvTemp != pvSentinel) { ulSrc = *pulSrc; xTmp = xAccum + xFrac; pulSrc = pulSrc + xInt + (xTmp < xAccum); *pvTemp = ulSrc; pvTemp++; xAccum = xTmp; } } inline void DNG_InitStretch(STRETCH* pStretch, ULONG ulDestWidth, ULONG ulSrcWidth, int left, int right) { pStretch->right = right; pStretch->left = left; ULONGLONG dx = ((((ULONGLONG) ulSrcWidth << 32) - 1) / (ULONGLONG) ulDestWidth) + 1; ULONGLONG x = (((ULONGLONG) ulSrcWidth << 32) / (ULONGLONG) ulDestWidth) >> 1; ULONG xInt = pStretch->xInt = (ULONG) (dx >> 32); ULONG xFrac = pStretch->xFrac = (ULONG) (dx & 0xFFFFFFFF); ULONG xAccum = (ULONG) (x & 0xFFFFFFFF); ULONG xTmp; ULONG xStart = (ULONG) (x >> 32); for (int i = 0; i < left; i++) { xTmp = xAccum + xFrac; xStart = xStart + xInt + (xTmp < xAccum); xAccum = xTmp; } pStretch->xStart = xStart; pStretch->xAccum = xAccum; } inline void DNG_DrawRow(DNGINTERNALDATA* pdng) { ULONG* pvDestLoc = pdng->pvDestBits; ULONG* pvSrcLoc = pdng->pvSrcBits; // Left if (pdng->cxClipMin < pdng->cxNewLeftWidth) { if (pdng->cxLeftWidth == pdng->cxNewLeftWidth) { CopyMemory(pvDestLoc + pdng->xMinLeft, pvSrcLoc + pdng->xMinLeft, (pdng->xMaxLeft - pdng->xMinLeft) * sizeof(ULONG)); } else { DNG_StretchRow(pvDestLoc, pvSrcLoc, &pdng->stretchLeft); } } pvDestLoc += pdng->cxNewLeftWidth; pvSrcLoc += pdng->cxLeftWidth; // Middle if (pdng->fShowMiddle) { if (pdng->xMinMiddle < pdng->xMaxMiddle) { if (pdng->fTileMode) { ULONG* pvTempSrc = pvSrcLoc; ULONG* pvTempDest = pvDestLoc; // Fill in Top Tile int xMin = pdng->xMinMiddle; int xDiff = xMin - pdng->cxLeftWidth; pvDestLoc += xDiff; int iTileSize = pdng->cxMiddleWidth - (xDiff % pdng->cxMiddleWidth); pvSrcLoc += xDiff % pdng->cxMiddleWidth; int xMax = pdng->xMaxMiddle; for (int x = xMin; x < xMax; x++, pvDestLoc++ , pvSrcLoc++) { *pvDestLoc = *pvSrcLoc; iTileSize--; if (iTileSize == 0) { iTileSize = pdng->cxMiddleWidth; pvSrcLoc -= iTileSize; } } pvDestLoc = pvTempDest; pvSrcLoc = pvTempSrc; } else { DNG_StretchRow(pvDestLoc, pvSrcLoc, &pdng->stretchMiddle); } } pvDestLoc += pdng->cxNewMiddleWidth; } pvSrcLoc += pdng->cxMiddleWidth; // Right if (pdng->cxClipMax > (pdng->iDestWidth - pdng->cxNewRightWidth)) { if (pdng->cxRightWidth == pdng->cxNewRightWidth) { CopyMemory(pvDestLoc + pdng->xMinRight, pvSrcLoc + pdng->xMinRight, (pdng->xMaxRight - pdng->xMinRight) * sizeof(ULONG)); } else { DNG_StretchRow(pvDestLoc, pvSrcLoc, &pdng->stretchRight); } } } inline void DNG_StretchCol(DNGINTERNALDATA* pdng, STRETCH * ps) { ULONG* pvOldDestBits = pdng->pvDestBits; ULONG* pvOldSrcBits = pdng->pvSrcBits; ULONG* pvTemp = pdng->pvDestBits + (DNG_BUF_WIDTH * ps->left); ULONG* pvSentinel = pdng->pvDestBits + (DNG_BUF_WIDTH * ps->right); ULONG xInt = ps->xInt; ULONG xFrac = ps->xFrac; ULONG xTmp; ULONG xAccum = ps->xAccum; ULONG * pulSrc = pdng->pvSrcBits + (pdng->iSrcBufWidth * ps->xStart); ULONG xDelta = 1; // force stretch on first scan while (pvTemp != pvSentinel) { if (xDelta != 0) { pdng->pvDestBits = pvTemp; pdng->pvSrcBits = pulSrc; DNG_DrawRow(pdng); } else { memcpy(pvTemp + pdng->cxClipMin, pvTemp + pdng->cxClipMin - DNG_BUF_WIDTH, pdng->iClipWidth * sizeof(ULONG)); } xTmp = xAccum + xFrac; xDelta = (xInt + (xTmp < xAccum)); pulSrc = pulSrc + (pdng->iSrcBufWidth * xDelta); pvTemp += DNG_BUF_WIDTH; xAccum = xTmp; } pdng->pvDestBits = pvOldDestBits; pdng->pvSrcBits = pvOldSrcBits; } HRESULT DrawNineGrid2(HDC hdc, PNGIMAGE pngiSrc, RECT* pRect, const RECT *prcClip, DWORD dwFlags) { // Store the static buffer static ULONG* s_pvBitsBuf = NULL; // Make sure that coordinates are valid; RECT rcDest = *pRect; if (rcDest.left > rcDest.right) { int xTemp = rcDest.left; rcDest.left = rcDest.right; rcDest.right = xTemp; } if (rcDest.top > rcDest.bottom) { int yTemp = rcDest.bottom; rcDest.bottom = rcDest.top; rcDest.top = yTemp; } RECT rcClip; if (prcClip) { IntersectRect(&rcClip, &rcDest, prcClip); } else { CopyRect(&rcClip, &rcDest); } HRESULT hr = S_OK; if ((pngiSrc->eSize == ST_TILE) || (pngiSrc->eSize == ST_STRETCH) || (pngiSrc->eSize == ST_TRUESIZE)) { ULONG* pvDestBits = NULL; int iDestWidth = rcDest.right - rcDest.left; int iDestHeight = rcDest.bottom - rcDest.top; int iClipWidth = rcClip.right - rcClip.left; int iClipHeight = rcClip.bottom - rcClip.top; if ((iClipWidth > DNG_BUF_WIDTH) || (iClipHeight > DNG_BUF_HEIGHT)) { // Divide the image into chunks smaller or equal to the buffer for (int y = rcClip.top; y < rcClip.bottom; y += DNG_BUF_HEIGHT) { for (int x = rcClip.left; x < rcClip.right; x += DNG_BUF_WIDTH) { RECT rcTemp = { x, y, x + DNG_BUF_WIDTH, y + DNG_BUF_HEIGHT }; RECT rcNewClip; IntersectRect(&rcNewClip, &rcTemp, &rcClip); DrawNineGrid2(hdc, pngiSrc, &rcDest, &rcNewClip, dwFlags); } } } else { EnterCriticalSection(&s_dngLock); // Use temporary buffer if (!s_hdcBuf) { DNG_CreateDIB(hdc, DNG_BUF_WIDTH, DNG_BUF_HEIGHT, &s_pvBitsBuf, &s_hdcBuf, &s_hbmBuf, &s_hbmOldBuf); SetLayout(s_hdcBuf, LAYOUT_BITMAPORIENTATIONPRESERVED); } pvDestBits = s_pvBitsBuf; if (s_hdcBuf) { DNGINTERNALDATA dng; dng.cxClipMin = rcClip.left - rcDest.left; dng.cxClipMax = rcClip.right - rcDest.left; int cyClipMin = rcClip.top - rcDest.top; int cyClipMax = rcClip.bottom - rcDest.top; pvDestBits += ((cyClipMin - (iDestHeight - DNG_BUF_HEIGHT)) * DNG_BUF_WIDTH) - dng.cxClipMin; int cxImage = rcClip.right - rcClip.left; int cyImage = rcClip.bottom - rcClip.top; if (pngiSrc->eSize == ST_TRUESIZE) { ULONG* pvDestLoc = pvDestBits + (iDestHeight - 1) * DNG_BUF_WIDTH; ULONG* pvSrcLoc = pngiSrc->pvBits + pngiSrc->iBufWidth * (pngiSrc->iHeight - 1); int yMin = cyClipMin; pvDestLoc -= yMin * DNG_BUF_WIDTH; pvSrcLoc -= yMin * pngiSrc->iBufWidth; int yMax = min(pngiSrc->iHeight, cyClipMax); int xMin = dng.cxClipMin; int xMax = min(pngiSrc->iWidth, dng.cxClipMax); if (xMax > xMin) { for (int y = yMin; y < yMax; y++, pvDestLoc -= DNG_BUF_WIDTH, pvSrcLoc -= pngiSrc->iBufWidth) { CopyMemory(pvDestLoc + xMin, pvSrcLoc + xMin, (xMax - xMin) * 4); } } cxImage = xMax - xMin; cyImage = yMax - yMin; } else { // Setup data dng.iDestWidth = iDestWidth; dng.iClipWidth = iClipWidth; dng.iSrcWidth = pngiSrc->iWidth; dng.iSrcBufWidth = pngiSrc->iBufWidth; dng.cxLeftWidth = pngiSrc->margin.cxLeftWidth; dng.cxRightWidth = pngiSrc->margin.cxRightWidth; dng.fTileMode = (pngiSrc->eSize == ST_TILE); // Calculate clip stuff // Pre-calc corner stretching variables dng.fShowMiddle = (iDestWidth - pngiSrc->margin.cxLeftWidth - pngiSrc->margin.cxRightWidth > 0); if (!dng.fShowMiddle) { dng.cxNewLeftWidth = (dng.cxLeftWidth + dng.cxRightWidth == 0) ? 0 : (dng.cxLeftWidth * dng.iDestWidth) / (dng.cxLeftWidth + dng.cxRightWidth); dng.cxNewRightWidth = dng.iDestWidth - dng.cxNewLeftWidth; } else { dng.cxNewLeftWidth = pngiSrc->margin.cxLeftWidth; dng.cxNewRightWidth = pngiSrc->margin.cxRightWidth; } // Pre-calc Left side variables dng.xMinLeft = dng.cxClipMin; dng.xMaxLeft = min(dng.cxNewLeftWidth, dng.cxClipMax); if (!dng.fShowMiddle && dng.cxNewLeftWidth) { DNG_InitStretch(&dng.stretchLeft, dng.cxNewLeftWidth, dng.cxLeftWidth, dng.xMinLeft, dng.xMaxLeft); } // Pre-calc Horizontal Middle Variables dng.cxMiddleWidth = dng.iSrcWidth - dng.cxLeftWidth - dng.cxRightWidth; dng.cxNewMiddleWidth = dng.iDestWidth - dng.cxNewLeftWidth - dng.cxNewRightWidth; dng.xMinMiddle = max(dng.cxNewLeftWidth, dng.cxClipMin); dng.xMaxMiddle = min(dng.cxNewLeftWidth + dng.cxNewMiddleWidth, dng.cxClipMax); if (dng.fShowMiddle) { DNG_InitStretch(&dng.stretchMiddle, dng.cxNewMiddleWidth, dng.cxMiddleWidth, dng.xMinMiddle - dng.cxNewLeftWidth, dng.xMaxMiddle - dng.cxNewLeftWidth); } // Pre-calc Right side variables dng.xMinRight = max(dng.iDestWidth - dng.cxNewRightWidth, dng.cxClipMin) - dng.cxNewLeftWidth - dng.cxNewMiddleWidth; dng.xMaxRight = min(dng.iDestWidth, dng.cxClipMax) - dng.cxNewLeftWidth - dng.cxNewMiddleWidth; if (!dng.fShowMiddle && dng.cxNewRightWidth) { DNG_InitStretch(&dng.stretchRight, dng.cxNewRightWidth, dng.cxRightWidth, dng.xMinRight, dng.xMaxRight); } BOOL fShowVertMiddle = (iDestHeight - pngiSrc->margin.cyTopHeight - pngiSrc->margin.cyBottomHeight > 0); int cyTopHeight = pngiSrc->margin.cyTopHeight; int cyBottomHeight = pngiSrc->margin.cyBottomHeight; int cyNewTopHeight; int cyNewBottomHeight; if (!fShowVertMiddle) { cyNewTopHeight = (cyTopHeight + cyBottomHeight == 0) ? 0 : (cyTopHeight * iDestHeight) / (cyTopHeight + cyBottomHeight); cyNewBottomHeight = iDestHeight - cyNewTopHeight; } else { cyNewTopHeight = cyTopHeight; cyNewBottomHeight = cyBottomHeight; } // Draw Bottom // Draw the scan line from (iDestHeight - cyNewBottomHeight) to less than iDestHeight, in screen coordinates int yMin = max(iDestHeight - cyNewBottomHeight, cyClipMin); int yMax = min(iDestHeight, cyClipMax); if (cyClipMax > iDestHeight - cyNewBottomHeight) { dng.pvDestBits = pvDestBits; dng.pvSrcBits = pngiSrc->pvBits; if (cyBottomHeight == cyNewBottomHeight) { int yDiff = yMin - (iDestHeight - cyNewBottomHeight); dng.pvDestBits += (cyBottomHeight - 1 - yDiff) * DNG_BUF_WIDTH; dng.pvSrcBits += (cyBottomHeight - 1 - yDiff) * dng.iSrcBufWidth; for (int y = yMin; y < yMax; y++, dng.pvDestBits -= DNG_BUF_WIDTH, dng.pvSrcBits -= dng.iSrcBufWidth) { DNG_DrawRow(&dng); } } else if (cyNewBottomHeight > 0) { STRETCH stretch; DNG_InitStretch(&stretch, cyNewBottomHeight, cyBottomHeight, cyNewBottomHeight - (yMax - iDestHeight + cyNewBottomHeight), cyNewBottomHeight - (yMin - iDestHeight + cyNewBottomHeight)); DNG_StretchCol(&dng, &stretch); } } // Draw Middle // Draw the scan line from cyNewTopHeight to less than (iDestHeight - cyNewBottomHeight), in screen coordinates if (fShowVertMiddle && (cyClipMin < iDestHeight - cyNewBottomHeight) && (cyClipMax > cyNewTopHeight)) { int cySrcTileSize = pngiSrc->iHeight - pngiSrc->margin.cyTopHeight - pngiSrc->margin.cyBottomHeight; int cyDestTileSize = iDestHeight - pngiSrc->margin.cyTopHeight - pngiSrc->margin.cyBottomHeight; dng.pvDestBits = pvDestBits + pngiSrc->margin.cyBottomHeight * DNG_BUF_WIDTH; dng.pvSrcBits = pngiSrc->pvBits + pngiSrc->margin.cyBottomHeight * pngiSrc->iBufWidth; int yMin = max(cyTopHeight, cyClipMin); if (dng.fTileMode) { // Start off tile dng.pvDestBits += (cyDestTileSize - 1) * DNG_BUF_WIDTH; dng.pvSrcBits += (cySrcTileSize - 1) * dng.iSrcBufWidth; int yDiff = yMin - cyTopHeight; dng.pvDestBits -= yDiff * DNG_BUF_WIDTH; int yOffset = (yDiff % cySrcTileSize); dng.pvSrcBits -= yOffset * dng.iSrcBufWidth; int iTileOffset = cySrcTileSize - yOffset; int yMax = min(yMin + min(cySrcTileSize, cyDestTileSize), min(iDestHeight - cyBottomHeight, cyClipMax)); for (int y = yMin; y < yMax; y++, dng.pvDestBits -= DNG_BUF_WIDTH, dng.pvSrcBits -= dng.iSrcBufWidth) { DNG_DrawRow(&dng); iTileOffset--; if (iTileOffset == 0) { iTileOffset = cySrcTileSize; dng.pvSrcBits += dng.iSrcBufWidth * cySrcTileSize; } } // Repeat tile pattern dng.pvSrcBits = dng.pvDestBits + (DNG_BUF_WIDTH * cySrcTileSize); yMin = yMax; yMax = min(iDestHeight - cyBottomHeight, cyClipMax); for (int y = yMin; y < yMax; y++, dng.pvDestBits -= DNG_BUF_WIDTH, dng.pvSrcBits -= DNG_BUF_WIDTH) { CopyMemory(dng.pvDestBits + dng.cxClipMin, dng.pvSrcBits + dng.cxClipMin, dng.iClipWidth * sizeof(ULONG)); } } else { int yMax = min(iDestHeight - cyBottomHeight, cyClipMax); STRETCH stretch; DNG_InitStretch(&stretch, cyDestTileSize, cySrcTileSize, cyDestTileSize - (yMax - cyTopHeight), cyDestTileSize - (yMin - cyTopHeight)); // Convert from screen coords to DIB coords DNG_StretchCol(&dng, &stretch); } } // Draw Top // Draw the scan line from 0 to less than cyNewTopHeight, in screen coordinates yMin = cyClipMin; yMax = min(cyNewTopHeight, cyClipMax); if (cyClipMin < cyNewTopHeight) { dng.pvDestBits = pvDestBits + (iDestHeight - cyNewTopHeight) * DNG_BUF_WIDTH; dng.pvSrcBits = pngiSrc->pvBits + (pngiSrc->iHeight - pngiSrc->margin.cyTopHeight) * pngiSrc->iBufWidth; if (cyTopHeight == cyNewTopHeight) { dng.pvDestBits += (cyTopHeight - 1 - yMin) * DNG_BUF_WIDTH; dng.pvSrcBits += (cyTopHeight - 1 - yMin) * dng.iSrcBufWidth; for (int y = yMin; y < yMax; y++, dng.pvDestBits -= DNG_BUF_WIDTH, dng.pvSrcBits -= dng.iSrcBufWidth) { DNG_DrawRow(&dng); } } else if (cyNewTopHeight > 0) { STRETCH stretch; DNG_InitStretch(&stretch, cyNewTopHeight, cyTopHeight, cyNewTopHeight - yMax, cyNewTopHeight - yMin); DNG_StretchCol(&dng, &stretch); } } } if ((dwFlags & DNG_MUSTFLIP) && ((pngiSrc->dwFlags & NGI_TRANS) || (pngiSrc->dwFlags & NGI_ALPHA))) { // Flip the buffer for (int y = 0; y < DNG_BUF_HEIGHT; y++) { ULONG* pvLeftBits = s_pvBitsBuf + (y * DNG_BUF_WIDTH); ULONG* pvRightBits = s_pvBitsBuf + (y * DNG_BUF_WIDTH) + iClipWidth - 1; for (int x = 0; x < (iClipWidth / 2); x++) { ULONG ulTemp = *pvLeftBits; *pvLeftBits = *pvRightBits; *pvRightBits = ulTemp; pvLeftBits++; pvRightBits--; } } } if (pngiSrc->dwFlags & NGI_ALPHA) { BLENDFUNCTION bf; bf.BlendOp = AC_SRC_OVER; bf.BlendFlags = 0; bf.SourceConstantAlpha = 255; bf.AlphaFormat = AC_SRC_ALPHA; GdiAlphaBlend(hdc, rcClip.left, rcClip.top, cxImage, cyImage, s_hdcBuf, 0, 0, cxImage, cyImage, bf); } else if (pngiSrc->dwFlags & NGI_TRANS) { GdiTransparentBlt(hdc, rcClip.left, rcClip.top, cxImage, cyImage, s_hdcBuf, 0, 0, cxImage, cyImage, pngiSrc->crTrans); } else { BitBlt(hdc, rcClip.left, rcClip.top, cxImage, cyImage, s_hdcBuf, 0, 0, SRCCOPY); } } else { hr = E_OUTOFMEMORY; } LeaveCriticalSection(&s_dngLock); } } return hr; }