以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- Accelerated Smooth Bitmap Resizing (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=47337) |
-- 作者:卷积内核 -- 发布时间:5/22/2007 11:49:00 AM -- Accelerated Smooth Bitmap Resizing When we want to resize a bitmap and also the resulting bitmap to be of the highest possible quality, the Win32 AIP function StretchBlt() is not of much help. There are already articles about this task in the CodeGuru sections, but the proposed algorithms take considerable amount of memory when used on large bitmaps - with dimensions of several thousand pixels on each axis. The function ScaleBitmap() uses an algorithm adjusted to use minimum additional memory and not sacrificing the overall performance. The prototype of the function is HBITMAP ScaleBitmap(HBITMAP hBmp, WORD wNewWidth, WORD wNewHeight); The only limitation is that both wNewWidth and wNewHeight should be greater or lesser than the dimensions of the original bitmap. The returned bitmap is compatible with the screen device context. The code below, let's mark it as variant A, produces images of good quality, but because of the large number of floating point calculations it is not as fast as we want. The main reserve for improving the performance of the routine is in the replacement of all floating-point calculations with integer ones. How could we do that without degradation of image quality? Actually we don't need more than 3 or 4 digits after the decimal point to produce the same result - the result after all is just a byte [0..255]. The obvious solution is to replace all floating numbers with numbers with fixed decimal point. By example we should replace the number 0.00345 with 345 and also divide the final result by 100000. OK - let's do it. Oops!!! In the code we should multiply these fixed-point numbers. Because we are limited to 4-byte integer numbers for performance reasons (means –2,147,483,648 to 2,147,483,647), we can't use 4 digits after the decimal point, because 256 * 10000 * 10000 exceeds 4-byte integer range and somewhere we will get a silent overflow. It seems that we should use the available 3 digits and be happy with that. This is not so bad, but who limits us to use just the powers of 10 to divide our fixed-point numbers. With the same success we can use the powers of 16, moreover the division by such a number equals to shifting right the number. Let's do some calculations. We have 4 bytes - that means 32 bits. We need 8 bits for the color values and the remaining of 24 bits leads us to decision to use 0x1000 (or 1 << 12) as the divisor for our fixed-point numbers. If the numbers are unsigned (and they are) we shouldn't have troubles with overflows. As a result we will have about 3,6 valid decimal digits in our fixed-point numbers - it's closer to 4 digits than just 3 digits. The second piece of code (variant B) uses this method to do all calculations with fixed-point integer numbers. This leads to obvious acceleration of the performance of code B compared to performance of code A, but how big is it? The following table represents the performance gain of code B over code A on processing a 305x435 pixels image. Result size / Original size 5% 10% 25% 50% 75% 100% 125% 150% 200% 300% Performance gain 24% 25% 34% 52% 59% 73% 67% 71% 75% 76% Considering that along with the calculations the code does also a lot of other work, we can conclude that the 4-byte integer calculations are 3 to 4 times faster than these with floating point numbers. Variant A // // Smooth bitmap resize // // Ivaylo Byalkov, November 16, 2000 // #include "stdafx.h" #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /////////////////////////////////////////////////////////// // helper function prototypes static BITMAPINFO *PrepareRGBBitmapInfo(WORD wWidth, WORD wHeight); static void ShrinkData(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight); static void EnlargeData(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight); /////////////////////////////////////////////////////////// // Main resize function HBITMAP ScaleBitmap(HBITMAP hBmp, WORD wNewWidth, WORD wNewHeight) { BITMAP bmp; ::GetObject(hBmp, sizeof(BITMAP), &bmp); // check for valid size if ((bmp.bmWidth > wNewWidth && bmp.bmHeight < wNewHeight) || bmp.bmWidth < wNewWidth && bmp.bmHeight > wNewHeight)) return NULL; HDC hDC = ::GetDC(NULL); BITMAPINFO *pbi = PrepareRGBBitmapInfo((WORD)bmp.bmWidth, (WORD)bmp.bmHeight); BYTE *pData = new BYTE[pbi->bmiHeader.biSizeImage]; ::GetDIBits(hDC, hBmp, 0, bmp.bmHeight, pData, pbi, DIB_RGB_COLORS); delete pbi; pbi = PrepareRGBBitmapInfo(wNewWidth, wNewHeight); BYTE *pData2 = new BYTE[pbi->bmiHeader.biSizeImage]; if(bmp.bmWidth >= wNewWidth && bmp.bmHeight >= wNewHeight) ShrinkData(pData, (WORD)bmp.bmWidth, (WORD)bmp.bmHeight, pData2, wNewWidth, wNewHeight); else EnlargeData(pData, (WORD)bmp.bmWidth, (WORD)bmp.bmHeight, pData2, wNewWidth, wNewHeight);
delete pData; HBITMAP hResBmp = ::CreateCompatibleBitmap(hDC, wNewWidth, wNewHeight); ::SetDIBits(hDC, hResBmp, 0, wNewHeight, pData2, pbi, DIB_RGB_COLORS); ::ReleaseDC(NULL, hDC); delete pbi; delete pData2; return hResBmp; }
|
-- 作者:卷积内核 -- 发布时间:5/22/2007 11:49:00 AM -- /////////////////////////////////////////////////////////// BITMAPINFO *PrepareRGBBitmapInfo(WORD wWidth, WORD wHeight) { BITMAPINFO *pRes = new BITMAPINFO; ::ZeroMemory(pRes, sizeof(BITMAPINFO)); pRes->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pRes->bmiHeader.biWidth = wWidth; pRes->bmiHeader.biHeight = wHeight; pRes->bmiHeader.biPlanes = 1; pRes->bmiHeader.biBitCount = 24; pRes->bmiHeader.biSizeImage = ((3 * wWidth + 3) & ~3) * wHeight; return pRes; } /////////////////////////////////////////////////////////// static float *CreateCoeff(int nLen, int nNewLen, BOOL bShrink) { int nSum = 0, nSum2; float *pRes = new float[2 * nLen]; float *pCoeff = pRes; float fNorm = (bShrink)? (float)nNewLen / nLen : 1; int nDenom = (bShrink)? nLen : nNewLen; ::ZeroMemory(pRes, 2 * nLen * sizeof(float)); for(int i = 0; i < nLen; i++, pCoeff += 2) { nSum2 = nSum + nNewLen; if(nSum2 > nLen) { *pCoeff = (float)(nLen - nSum) / nDenom; pCoeff[1] = (float)(nSum2 - nLen) / nDenom; nSum2 -= nLen; } else { *pCoeff = fNorm; if(nSum2 == nLen) { pCoeff[1] = -1; nSum2 = 0; } } nSum = nSum2; } return pRes; } /////////////////////////////////////////////////////////// #define F_DELTA 0.0001f void ShrinkData(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight) { BYTE *pLine = pInBuff, *pPix; BYTE *pOutLine = pOutBuff; DWORD dwInLn = (3 * wWidth + 3) & ~3; DWORD dwOutLn = (3 * wNewWidth + 3) & ~3; int x, y, i, ii; BOOL bCrossRow, bCrossCol; float *pRowCoeff = CreateCoeff(wWidth, wNewWidth, TRUE); float *pColCoeff = CreateCoeff(wHeight, wNewHeight, TRUE); float fTmp, *pXCoeff, *pYCoeff = pColCoeff; DWORD dwBuffLn = 3 * wNewWidth * sizeof(float); float *fBuff = new float[6 * wNewWidth]; float *fCurrLn = fBuff, *fCurrPix, *fNextLn = fBuff + 3 * wNewWidth, *fNextPix; ::ZeroMemory(fBuff, 2 * dwBuffLn); y = 0; while(y < wNewHeight) { pPix = pLine; pLine += dwInLn; fCurrPix = fCurrLn; fNextPix = fNextLn; x = 0; pXCoeff = pRowCoeff; bCrossRow = pYCoeff[1] > F_DELTA; while(x < wNewWidth) { fTmp = *pXCoeff * *pYCoeff; for(i = 0; i < 3; i++) fCurrPix[i] += fTmp * pPix[i]; bCrossCol = pXCoeff[1] > F_DELTA; if(bCrossCol) { fTmp = pXCoeff[1] * *pYCoeff; for(i = 0, ii = 3; i < 3; i++, ii++) fCurrPix[ii] += fTmp * pPix[i]; } if(bCrossRow) { fTmp = *pXCoeff * pYCoeff[1]; for(i = 0; i < 3; i++) fNextPix[i] += fTmp * pPix[i]; if(bCrossCol) { fTmp = pXCoeff[1] * pYCoeff[1]; for(i = 0, ii = 3; i < 3; i++, ii++) fNextPix[ii] += fTmp * pPix[i]; } } if(fabs(pXCoeff[1]) > F_DELTA) { x++; fCurrPix += 3; fNextPix += 3; } pXCoeff += 2; pPix += 3; } if(fabs(pYCoeff[1]) > F_DELTA) { // set result line fCurrPix = fCurrLn; pPix = pOutLine; for(i = 3 * wNewWidth; i > 0; i--, fCurrPix++, pPix++) *pPix = (BYTE)*fCurrPix; // prepare line buffers fCurrPix = fNextLn; fNextLn = fCurrLn; fCurrLn = fCurrPix; ::ZeroMemory(fNextLn, dwBuffLn); y++; pOutLine += dwOutLn; } pYCoeff += 2; } delete [] pRowCoeff; delete [] pColCoeff; delete [] fBuff; }
|
-- 作者:卷积内核 -- 发布时间:5/22/2007 11:49:00 AM -- /////////////////////////////////////////////////////////// void EnlargeData(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight) { BYTE *pLine = pInBuff, *pPix = pLine, *pPixOld, *pUpPix, *pUpPixOld; BYTE *pOutLine = pOutBuff, *pOutPix; DWORD dwInLn = (3 * wWidth + 3) & ~3; DWORD dwOutLn = (3 * wNewWidth + 3) & ~3; int x, y, i; BOOL bCrossRow, bCrossCol; float *pRowCoeff = CreateCoeff(wNewWidth, wWidth, FALSE); float *pColCoeff = CreateCoeff(wNewHeight, wHeight, FALSE); float fTmp, fPtTmp[3], *pXCoeff, *pYCoeff = pColCoeff; y = 0; while(y < wHeight) { bCrossRow = pYCoeff[1] > F_DELTA; x = 0; pXCoeff = pRowCoeff; pOutPix = pOutLine; pOutLine += dwOutLn; pUpPix = pLine; if(fabs(pYCoeff[1]) > F_DELTA) { y++; pLine += dwInLn; pPix = pLine; } while(x < wWidth) { bCrossCol = pXCoeff[1] > F_DELTA; pUpPixOld = pUpPix; pPixOld = pPix; if(fabs(pXCoeff[1]) > F_DELTA) { x++; pUpPix += 3; pPix += 3; } fTmp = *pXCoeff * *pYCoeff; for(i = 0; i < 3; i++) fPtTmp[i] = fTmp * pUpPixOld[i]; if(bCrossCol) { fTmp = pXCoeff[1] * *pYCoeff; for(i = 0; i < 3; i++) fPtTmp[i] += fTmp * pUpPix[i]; } if(bCrossRow) { fTmp = *pXCoeff * pYCoeff[1]; for(i = 0; i < 3; i++) fPtTmp[i] += fTmp * pPixOld[i]; if(bCrossCol) { fTmp = pXCoeff[1] * pYCoeff[1]; for(i = 0; i < 3; i++) fPtTmp[i] += fTmp * pPix[i]; } } for(i = 0; i < 3; i++, pOutPix++) *pOutPix = (BYTE)fPtTmp[i]; pXCoeff += 2; } pYCoeff += 2; } delete [] pRowCoeff; delete [] pColCoeff; } // end src Variant B // // Functions for smooth bitmap resize // // Improvement: float calculations changed to int. // // Ivaylo Byalkov, January 24, 2000 // e-mail: ivob@i-n.net // #include "stdafx.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /////////////////////////////////////////////////////////// // helper function prototypes static BITMAPINFO *PrepareRGBBitmapInfo(WORD wWidth, WORD wHeight); static void ShrinkDataInt(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight); static void EnlargeDataInt(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight); /////////////////////////////////////////////////////////// // Main resize function HBITMAP ScaleBitmapInt(HBITMAP hBmp, WORD wNewWidth, WORD wNewHeight) { BITMAP bmp; ::GetObject(hBmp, sizeof(BITMAP), &bmp); // check for valid size if((bmp.bmWidth > wNewWidth && bmp.bmHeight < wNewHeight) || bmp.bmWidth < wNewWidth && bmp.bmHeight > wNewHeight)) return NULL; HDC hDC = ::GetDC(NULL); BITMAPINFO *pbi = PrepareRGBBitmapInfo((WORD)bmp.bmWidth, (WORD)bmp.bmHeight); BYTE *pData = new BYTE[pbi->bmiHeader.biSizeImage]; ::GetDIBits(hDC, hBmp, 0, bmp.bmHeight, pData, pbi, DIB_RGB_COLORS); delete pbi; pbi = PrepareRGBBitmapInfo(wNewWidth, wNewHeight); BYTE *pData2 = new BYTE[pbi->bmiHeader.biSizeImage]; if(bmp.bmWidth >= wNewWidth && bmp.bmHeight >= wNewHeight) ShrinkDataInt(pData, (WORD)bmp.bmWidth, (WORD)bmp.bmHeight, pData2, wNewWidth, wNewHeight); else EnlargeDataInt(pData, (WORD)bmp.bmWidth, (WORD)bmp.bmHeight, pData2, wNewWidth, wNewHeight); delete pData; HBITMAP hResBmp = ::CreateCompatibleBitmap(hDC, wNewWidth, wNewHeight); ::SetDIBits(hDC, hResBmp, 0, wNewHeight, pData2, pbi, DIB_RGB_COLORS); ::ReleaseDC(NULL, hDC); delete pbi; delete pData2; return hResBmp; }
|
-- 作者:卷积内核 -- 发布时间:5/22/2007 11:50:00 AM -- /////////////////////////////////////////////////////////// BITMAPINFO *PrepareRGBBitmapInfo(WORD wWidth, WORD wHeight) { BITMAPINFO *pRes = new BITMAPINFO; ::ZeroMemory(pRes, sizeof(BITMAPINFO)); pRes->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pRes->bmiHeader.biWidth = wWidth; pRes->bmiHeader.biHeight = wHeight; pRes->bmiHeader.biPlanes = 1; pRes->bmiHeader.biBitCount = 24; pRes->bmiHeader.biSizeImage = ((3 * wWidth + 3) & ~3) * wHeight;
return pRes; } /////////////////////////////////////////////////////////// static int *CreateCoeffInt(int nLen, int nNewLen, BOOL bShrink) { int nSum = 0, nSum2; int *pRes = new int[2 * nLen]; int *pCoeff = pRes; int nNorm = (bShrink) ? (nNewLen << 12) / nLen : 0x1000; int nDenom = (bShrink)? nLen : nNewLen; ::ZeroMemory(pRes, 2 * nLen * sizeof(int)); for(int i = 0; i < nLen; i++, pCoeff += 2) { nSum2 = nSum + nNewLen; if(nSum2 > nLen) { *pCoeff = ((nLen - nSum) << 12) / nDenom; pCoeff[1] = ((nSum2 - nLen) << 12) / nDenom; nSum2 -= nLen; } else { *pCoeff = nNorm; if(nSum2 == nLen) { pCoeff[1] = -1; nSum2 = 0; } } nSum = nSum2; }
return pRes; } /////////////////////////////////////////////////////////// void ShrinkDataInt(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight) { BYTE *pLine = pInBuff, *pPix; BYTE *pOutLine = pOutBuff; DWORD dwInLn = (3 * wWidth + 3) & ~3; DWORD dwOutLn = (3 * wNewWidth + 3) & ~3; int x, y, i, ii; BOOL bCrossRow, bCrossCol; int *pRowCoeff = CreateCoeffInt(wWidth, wNewWidth, TRUE); int *pColCoeff = CreateCoeffInt(wHeight, wNewHeight, TRUE); int *pXCoeff, *pYCoeff = pColCoeff; DWORD dwBuffLn = 3 * wNewWidth * sizeof(DWORD); DWORD *pdwBuff = new DWORD[6 * wNewWidth]; DWORD *pdwCurrLn = pdwBuff, *pdwCurrPix, *pdwNextLn = pdwBuff + 3 * wNewWidth; DWORD dwTmp, *pdwNextPix; ::ZeroMemory(pdwBuff, 2 * dwBuffLn); y = 0; while(y < wNewHeight) { pPix = pLine; pLine += dwInLn; pdwCurrPix = pdwCurrLn; pdwNextPix = pdwNextLn; x = 0; pXCoeff = pRowCoeff; bCrossRow = pYCoeff[1] > 0; while(x < wNewWidth) { dwTmp = *pXCoeff * *pYCoeff; for(i = 0; i < 3; i++) pdwCurrPix[i] += dwTmp * pPix[i]; bCrossCol = pXCoeff[1] > 0; if(bCrossCol) { dwTmp = pXCoeff[1] * *pYCoeff; for(i = 0, ii = 3; i < 3; i++, ii++) pdwCurrPix[ii] += dwTmp * pPix[i]; } if(bCrossRow) { dwTmp = *pXCoeff * pYCoeff[1]; for(i = 0; i < 3; i++) pdwNextPix[i] += dwTmp * pPix[i]; if(bCrossCol) { dwTmp = pXCoeff[1] * pYCoeff[1]; for(i = 0, ii = 3; i < 3; i++, ii++) pdwNextPix[ii] += dwTmp * pPix[i]; } } if(pXCoeff[1]) { x++; pdwCurrPix += 3; pdwNextPix += 3; } pXCoeff += 2; pPix += 3; } if(pYCoeff[1]) { // set result line pdwCurrPix = pdwCurrLn; pPix = pOutLine; for(i = 3 * wNewWidth; i > 0; i--, pdwCurrPix++, pPix++) *pPix = ((LPBYTE)pdwCurrPix)[3]; // prepare line buffers pdwCurrPix = pdwNextLn; pdwNextLn = pdwCurrLn; pdwCurrLn = pdwCurrPix; ::ZeroMemory(pdwNextLn, dwBuffLn); y++; pOutLine += dwOutLn; } pYCoeff += 2; } delete [] pRowCoeff; delete [] pColCoeff; delete [] pdwBuff; }
|
-- 作者:卷积内核 -- 发布时间:5/22/2007 11:51:00 AM -- /////////////////////////////////////////////////////////// void EnlargeDataInt(BYTE *pInBuff, WORD wWidth, WORD wHeight, BYTE *pOutBuff, WORD wNewWidth, WORD wNewHeight) { BYTE *pLine = pInBuff, *pPix = pLine, *pPixOld, *pUpPix, *pUpPixOld; BYTE *pOutLine = pOutBuff, *pOutPix; DWORD dwInLn = (3 * wWidth + 3) & ~3; DWORD dwOutLn = (3 * wNewWidth + 3) & ~3; int x, y, i; BOOL bCrossRow, bCrossCol; int *pRowCoeff = CreateCoeffInt(wNewWidth, wWidth, FALSE); int *pColCoeff = CreateCoeffInt(wNewHeight, wHeight, FALSE); int *pXCoeff, *pYCoeff = pColCoeff; DWORD dwTmp, dwPtTmp[3]; y = 0; while(y < wHeight) { bCrossRow = pYCoeff[1] > 0; x = 0; pXCoeff = pRowCoeff; pOutPix = pOutLine; pOutLine += dwOutLn; pUpPix = pLine; if(pYCoeff[1]) { y++; pLine += dwInLn; pPix = pLine; } while(x < wWidth) { bCrossCol = pXCoeff[1] > 0; pUpPixOld = pUpPix; pPixOld = pPix; if(pXCoeff[1]) { x++; pUpPix += 3; pPix += 3; }
dwTmp = *pXCoeff * *pYCoeff;
for(i = 0; i < 3; i++) dwPtTmp[i] = dwTmp * pUpPixOld[i];
if(bCrossCol) { dwTmp = pXCoeff[1] * *pYCoeff; for(i = 0; i < 3; i++) dwPtTmp[i] += dwTmp * pUpPix[i]; } if(bCrossRow) { dwTmp = *pXCoeff * pYCoeff[1]; for(i = 0; i < 3; i++) dwPtTmp[i] += dwTmp * pPixOld[i]; if(bCrossCol) { dwTmp = pXCoeff[1] * pYCoeff[1]; for(i = 0; i < 3; i++) dwPtTmp[i] += dwTmp * pPix[i]; } }
for(i = 0; i < 3; i++, pOutPix++) *pOutPix = ((LPBYTE)(dwPtTmp + i))[3];
pXCoeff += 2; } pYCoeff += 2; } delete [] pRowCoeff; delete [] pColCoeff; } // end src
|
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
108.398ms |