|
|
#include "precomp.h"
#include "ocmm.h"
#include "thumbutil.h"
typedef UCHAR BGR3[3];
class CThumbnailMaker { public: CThumbnailMaker(); ~CThumbnailMaker();
void Scale(BGR3 *pDst, UINT uiDstWidth, int iDstStep, const BGR3 *pSrc, UINT uiSrcWidth, int iSrcStep); HRESULT Init(UINT uiDstWidth, UINT uiDstHeight, UINT uiSrcWidth, UINT uiSrcHeight); HRESULT AddScanline(UCHAR *pucSrc, UINT uiY); HRESULT AddDIB(BITMAPINFO *pBMI); HRESULT AddDIBSECTION(BITMAPINFO *pBMI, void *pBits); HRESULT GetBITMAPINFO(BITMAPINFO **ppBMInfo, DWORD *pdwSize); HRESULT GetSharpenedBITMAPINFO(UINT uiSharpPct, BITMAPINFO **ppBMInfo, DWORD *pdwSize);
private: UINT _uiDstWidth, _uiDstHeight; UINT _uiSrcWidth, _uiSrcHeight; BGR3 *_pImH; };
CThumbnailMaker::CThumbnailMaker() { _pImH = NULL; }
CThumbnailMaker::~CThumbnailMaker() { if (_pImH) delete[] _pImH; }
HRESULT CThumbnailMaker::Init(UINT uiDstWidth, UINT uiDstHeight, UINT uiSrcWidth, UINT uiSrcHeight) { _uiDstWidth = uiDstWidth; _uiDstHeight = uiDstHeight; _uiSrcWidth = uiSrcWidth; _uiSrcHeight = uiSrcHeight;
if (_uiDstWidth < 1 || _uiDstHeight < 1 || _uiSrcWidth < 1 || _uiSrcHeight < 1) return E_INVALIDARG;
if (_pImH) delete[] _pImH;
_pImH = new BGR3[_uiDstWidth * _uiSrcHeight]; if (_pImH == NULL) return E_OUTOFMEMORY;
return S_OK; }
void CThumbnailMaker::Scale( BGR3 *pDst, UINT dxDst, int iDstBytStep, const BGR3 *pSrc, UINT dxSrc, int iSrcBytStep) { int mnum = dxSrc; int mden = dxDst;
// Scaling up, use a triangle filter.
if (mden >= mnum) { int frac = 0;
// Adjust the slope so that we calculate the fraction of the
// "next" pixel to use (i.e. should be 0 for the first and
// last dst pixel).
--mnum; if (--mden == 0) mden = 0; // avoid div by 0
BGR3 *pSrc1 = (BGR3 *)(((UCHAR *)pSrc) + iSrcBytStep);
for (UINT x = 0; x < dxDst; x++) { if (frac == 0) { (*pDst)[0] = (*pSrc)[0]; (*pDst)[1] = (*pSrc)[1]; (*pDst)[2] = (*pSrc)[2]; } else { (*pDst)[0] = ((mden - frac) * (*pSrc)[0] + frac * (*pSrc1)[0]) / mden; (*pDst)[1] = ((mden - frac) * (*pSrc)[1] + frac * (*pSrc1)[1]) / mden; (*pDst)[2] = ((mden - frac) * (*pSrc)[2] + frac * (*pSrc1)[2]) / mden; }
pDst = (BGR3 *)((UCHAR *)pDst + iDstBytStep);
frac += mnum; if (frac >= mden) { frac -= mden; pSrc = (BGR3 *)((UCHAR *)pSrc + iSrcBytStep); pSrc1 = (BGR3 *)((UCHAR *)pSrc1 + iSrcBytStep); } } } // Scaling down, use a box filter.
else { int frac = 0;
for (UINT x = 0; x < dxDst; x++) { UINT uiSum[3] = {0, 0, 0}; UINT uiCnt = 0;
frac += mnum; while (frac >= mden) { uiSum[0] += (*pSrc)[0]; uiSum[1] += (*pSrc)[1]; uiSum[2] += (*pSrc)[2]; uiCnt++;
frac -= mden; pSrc = (BGR3 *)((UCHAR *)pSrc + iSrcBytStep); }
(*pDst)[0] = uiSum[0] / uiCnt; (*pDst)[1] = uiSum[1] / uiCnt; (*pDst)[2] = uiSum[2] / uiCnt;
pDst = (BGR3 *)((UCHAR *)pDst + iDstBytStep); } } }
//
// For AddScanline, we scale the input horizontally into our temporary
// image buffer.
//
HRESULT CThumbnailMaker::AddScanline(UCHAR *pSrc, UINT uiY) { if (pSrc == NULL || uiY >= _uiSrcHeight) return E_INVALIDARG;
Scale(_pImH + uiY * _uiDstWidth, _uiDstWidth, sizeof(BGR3), (BGR3 *)pSrc, _uiSrcWidth, sizeof(BGR3));
return S_OK; }
// For GetBITMAPINFO, we complete the scaling vertically and return the
// result as a DIB.
HRESULT CThumbnailMaker::GetBITMAPINFO(BITMAPINFO **ppBMInfo, DWORD *pdwSize) { *ppBMInfo = NULL;
DWORD dwBPL = (((_uiDstWidth * 24) + 31) >> 3) & ~3; DWORD dwTotSize = sizeof(BITMAPINFOHEADER) + dwBPL * _uiDstHeight;
BITMAPINFO *pBMI = (BITMAPINFO *)CoTaskMemAlloc(dwTotSize); if (pBMI == NULL) return E_OUTOFMEMORY;
BITMAPINFOHEADER *pBMIH = &pBMI->bmiHeader; pBMIH->biSize = sizeof(*pBMIH); pBMIH->biWidth = _uiDstWidth; pBMIH->biHeight = _uiDstHeight; pBMIH->biPlanes = 1; pBMIH->biBitCount = 24; pBMIH->biCompression = BI_RGB; pBMIH->biXPelsPerMeter = 0; pBMIH->biYPelsPerMeter = 0; pBMIH->biSizeImage = dwBPL * _uiDstHeight; pBMIH->biClrUsed = 0; pBMIH->biClrImportant = 0;
UCHAR *pDst = (UCHAR *)pBMIH + pBMIH->biSize + (_uiDstHeight - 1) * dwBPL;
for (UINT x = 0; x < _uiDstWidth; x++) { Scale((BGR3 *)pDst + x, _uiDstHeight, -(int)dwBPL, _pImH + x, _uiSrcHeight, _uiDstWidth * sizeof(BGR3)); }
*ppBMInfo = pBMI; *pdwSize = dwTotSize;
return S_OK; }
HRESULT CThumbnailMaker::GetSharpenedBITMAPINFO(UINT uiSharpPct, BITMAPINFO **ppBMInfo, DWORD *pdwSize) { #define SCALE 10000
if (uiSharpPct > 100) return E_INVALIDARG;
// Get the unsharpened bitmap.
DWORD dwSize; HRESULT hr = GetBITMAPINFO(ppBMInfo, &dwSize); if (FAILED(hr)) return hr;
*pdwSize = dwSize;
// Create a duplicate to serve as the original.
BITMAPINFO *pBMISrc = (BITMAPINFO *)new UCHAR[dwSize]; if (pBMISrc == NULL) { delete *ppBMInfo; return E_OUTOFMEMORY; } memcpy(pBMISrc, *ppBMInfo, dwSize);
int bpl = (pBMISrc->bmiHeader.biWidth * 3 + 3) & ~3;
//
// Sharpen inside a 1 pixel border
//
UCHAR *pucDst = (UCHAR *)*ppBMInfo + sizeof(BITMAPINFOHEADER); UCHAR *pucSrc[3]; pucSrc[0] = (UCHAR *)pBMISrc + sizeof(BITMAPINFOHEADER); pucSrc[1] = pucSrc[0] + bpl; pucSrc[2] = pucSrc[1] + bpl;
int wdiag = (10355 * uiSharpPct) / 100; int wadj = (14645 * uiSharpPct) / 100; int wcent = 4 * (wdiag + wadj);
for (int y = 1; y < pBMISrc->bmiHeader.biHeight-1; ++y) { for (int x = 3*(pBMISrc->bmiHeader.biWidth-2); x >= 3; --x) { int v = pucDst[x] + (pucSrc[1][x] * wcent - ((pucSrc[0][x - 3] + pucSrc[0][x + 3] + pucSrc[2][x - 3] + pucSrc[2][x + 3]) * wdiag + (pucSrc[0][x] + pucSrc[1][x - 3] + pucSrc[1][x + 3] + pucSrc[2][x]) * wadj)) / SCALE;
pucDst[x] = v < 0 ? 0 : v > 255 ? 255 : v; }
pucDst += bpl; pucSrc[0] = pucSrc[1]; pucSrc[1] = pucSrc[2]; pucSrc[2] += bpl; }
delete[] pBMISrc;
return S_OK; #undef SCALE
}
HRESULT ThumbnailMaker_Create(CThumbnailMaker **ppThumbMaker) { *ppThumbMaker = new CThumbnailMaker; return *ppThumbMaker ? S_OK : E_OUTOFMEMORY; }
HRESULT CThumbnailMaker::AddDIB(BITMAPINFO *pBMI) { int ncolors = pBMI->bmiHeader.biClrUsed; if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8) ncolors = 1 << pBMI->bmiHeader.biBitCount; if (pBMI->bmiHeader.biBitCount == 16 || pBMI->bmiHeader.biBitCount == 32) { if (pBMI->bmiHeader.biCompression == BI_BITFIELDS) { ncolors = 3; } }
UCHAR *pBits = (UCHAR *)&pBMI->bmiColors[0] + ncolors * sizeof(RGBQUAD);
return AddDIBSECTION(pBMI, (void *) pBits); }
HRESULT CThumbnailMaker::AddDIBSECTION(BITMAPINFO *pBMI, void *pBits) { RGBQUAD *pRGBQ, *pQ; UCHAR *pucBits0, *pucBits, *pB, *pucBits240, *pucBits24, *pB24; int bpl; int x, y, ncolors; ULONG rmask, gmask, bmask; int rshift, gshift, bshift; HRESULT hr;
//
// Make sure that thumbnail maker has been properly initialized.
//
if (pBMI == NULL) return E_INVALIDARG;
if (pBMI->bmiHeader.biWidth != (LONG)_uiSrcWidth || pBMI->bmiHeader.biHeight != (LONG)_uiSrcHeight) return E_INVALIDARG;
//
// Don't handle RLE.
//
if (pBMI->bmiHeader.biCompression != BI_RGB && pBMI->bmiHeader.biCompression != BI_BITFIELDS) return E_INVALIDARG;
pRGBQ = (RGBQUAD *)&pBMI->bmiColors[0];
ncolors = pBMI->bmiHeader.biClrUsed; if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8) ncolors = 1 << pBMI->bmiHeader.biBitCount;
//
// Decode 16/32bpp with masks.
//
if (pBMI->bmiHeader.biBitCount == 16 || pBMI->bmiHeader.biBitCount == 32) { if (pBMI->bmiHeader.biCompression == BI_BITFIELDS) { rmask = ((ULONG *)pRGBQ)[0]; gmask = ((ULONG *)pRGBQ)[1]; bmask = ((ULONG *)pRGBQ)[2]; ncolors = 3; } else if (pBMI->bmiHeader.biBitCount == 16) { rmask = 0x7c00; gmask = 0x03e0; bmask = 0x001f; } else /* 32 */ { rmask = 0xff0000; gmask = 0x00ff00; bmask = 0x0000ff; }
for (rshift = 0; (rmask & 1) == 0; rmask >>= 1, ++rshift); if (rmask == 0) rmask = 1; for (gshift = 0; (gmask & 1) == 0; gmask >>= 1, ++gshift); if (gmask == 0) gmask = 1; for (bshift = 0; (bmask & 1) == 0; bmask >>= 1, ++bshift); if (bmask == 0) bmask = 1; }
bpl = ((pBMI->bmiHeader.biBitCount * _uiSrcWidth + 31) >> 3) & ~3;
pucBits0 = (UCHAR *) pBits; pucBits = pucBits0;
if (pBMI->bmiHeader.biBitCount == 24) pucBits240 = pucBits; else { int bpl24 = (_uiSrcWidth * 3 + 3) & ~3;
pucBits240 = new UCHAR[bpl24]; if (pucBits240 == NULL) return E_OUTOFMEMORY; } pucBits24 = pucBits240;
hr = S_OK;
for (y = 0; y < (int)_uiSrcHeight; ++y) { pB = pucBits; pB24 = pucBits24;
switch (pBMI->bmiHeader.biBitCount) { case 1: for (x = _uiSrcWidth; x >= 8; x -= 8) { pQ = &pRGBQ[(*pB >> 7) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[(*pB >> 6) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[(*pB >> 5) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[(*pB >> 4) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[(*pB >> 3) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[(*pB >> 2) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[(*pB >> 1) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[(*pB++) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed; }
if (x > 0) { int shf = 8;
do { pQ = &pRGBQ[(*pB >> --shf) & 1]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed; } while (--x); }
break;
case 4: for (x = _uiSrcWidth; x >= 2; x -= 2) { pQ = &pRGBQ[(*pB >> 4) & 0xf]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
pQ = &pRGBQ[*pB++ & 0xf]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed; }
if (x > 0) { pQ = &pRGBQ[(*pB >> 4) & 0xf]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed;
if (x > 1) { pQ = &pRGBQ[*pB & 0xf]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed; } }
break;
case 8: for (x = _uiSrcWidth; x--;) { pQ = &pRGBQ[*pB++]; *pB24++ = pQ->rgbBlue; *pB24++ = pQ->rgbGreen; *pB24++ = pQ->rgbRed; }
break;
case 16: { USHORT *pW = (USHORT *)pucBits;
for (x = _uiSrcWidth; x--;) { ULONG w = *pW++;
*pB24++ = (UCHAR) ((((w >> bshift) & bmask) * 255) / bmask); *pB24++ = (UCHAR) ((((w >> gshift) & gmask) * 255) / gmask); *pB24++ = (UCHAR) ((((w >> rshift) & rmask) * 255) / rmask); }
break; }
case 24: pucBits24 = pucBits; break;
case 32: { ULONG *pD;
pD = (ULONG *)pucBits;
for (x = _uiSrcWidth; x--;) { ULONG d = *pD++;
*pB24++ = (UCHAR) ((((d >> bshift) & bmask) * 255) / bmask); *pB24++ = (UCHAR) ((((d >> gshift) & gmask) * 255) / gmask); *pB24++ = (UCHAR) ((((d >> rshift) & rmask) * 255) / rmask); }
break; }
default: delete[] pucBits24; return E_INVALIDARG; }
hr = AddScanline(pucBits24, (_uiSrcHeight-1) - y); if (FAILED(hr)) break;
pucBits += bpl; }
if (pucBits240 != pucBits0) delete[] pucBits240;
return hr; }
UINT CalcImageSize(const SIZE *prgSize, DWORD dwClrDepth) { UINT uSize = prgSize->cx * dwClrDepth; uSize *= (prgSize->cy < 0) ? (- prgSize->cy) : prgSize->cy; // divide by 8
UINT uRetVal = uSize >> 3;
if (uSize & 7) { uRetVal++; }
return uRetVal; }
// exported as helper for thumbnail implementations (used to come from thumbvw.dll)
//
// this code also currently lives in shell32. that should be converted to
// import these APIs (or expose via COM object)
STDAPI_(BOOL) ConvertDIBSECTIONToThumbnail(BITMAPINFO *pbi, void *pBits, HBITMAP *phBmpThumbnail, const SIZE *prgSize, DWORD dwRecClrDepth, HPALETTE hpal, UINT uiSharpPct, BOOL fOrigSize) { BITMAPINFO *pbiScaled = pbi, *pbiUsed = pbi; BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)pbi; BOOL bRetVal = FALSE, bInverted = FALSE; RECT rect; HRESULT hr; void *pScaledBits = pBits;
// the scaling code doesn't handle inverted bitmaps, so we treat
// them as if they were normal, by inverting the height here and
// then setting it back before doing a paint.
if (pbi->bmiHeader.biHeight < 0) { pbi->bmiHeader.biHeight *= -1; bInverted = TRUE; }
rect.left = 0; rect.top = 0; rect.right = pbih->biWidth; rect.bottom = pbih->biHeight; CalculateAspectRatio(prgSize, &rect);
// only bother with the scaling and sharpening if we are messing with the size...
if ((rect.right - rect.left != pbih->biWidth) || (rect.bottom - rect.top != pbih->biHeight)) { CThumbnailMaker *pThumbMaker; hr = ThumbnailMaker_Create(&pThumbMaker); if (SUCCEEDED(hr)) { // initialize thumbnail maker.
hr = pThumbMaker->Init(rect.right - rect.left, rect.bottom - rect.top, pbi->bmiHeader.biWidth, abs(pbi->bmiHeader.biHeight)); if (SUCCEEDED(hr)) { // scale image.
hr = pThumbMaker->AddDIBSECTION(pbiUsed, pBits); if (SUCCEEDED(hr)) { DWORD dwSize; hr = pThumbMaker->GetSharpenedBITMAPINFO(uiSharpPct, &pbiScaled, &dwSize); if (SUCCEEDED(hr)) { pScaledBits = (LPBYTE)pbiScaled + sizeof(BITMAPINFOHEADER); } } } delete pThumbMaker; }
if (FAILED(hr)) { return FALSE; } }
// set the height back to negative if that's the way it was before.
if (bInverted == TRUE) pbiScaled->bmiHeader.biHeight *= -1;
// now if they have asked for origsize rather than the boxed one, and the colour depth is OK, then
// return it...
if (fOrigSize && pbiScaled->bmiHeader.biBitCount <= dwRecClrDepth) { SIZE rgCreateSize = { pbiScaled->bmiHeader.biWidth, pbiScaled->bmiHeader.biHeight }; void *pNewBits; // turn the PbiScaled DIB into a HBITMAP...., note we pass the old biInfo so that it can get the palette form
// it if need be.
bRetVal = CreateSizedDIBSECTION(&rgCreateSize, pbiScaled->bmiHeader.biBitCount, NULL, pbiScaled, phBmpThumbnail, NULL, &pNewBits);
if (bRetVal) { // copy the image data accross...
CopyMemory(pNewBits, pScaledBits, CalcImageSize(&rgCreateSize, pbiScaled->bmiHeader.biBitCount)); } return bRetVal; } bRetVal = FactorAspectRatio(pbiScaled, pScaledBits, prgSize, rect, dwRecClrDepth, hpal, fOrigSize, GetSysColor(COLOR_WINDOW), phBmpThumbnail);
if (pbiScaled != pbi) { // free the allocated image...
CoTaskMemFree(pbiScaled); }
return bRetVal; }
// This function makes no assumption about whether the thumbnail is square, so
// it calculates the scaling ratio for both dimensions and the uses that as
// the scaling to maintain the aspect ratio.
//
// WINDOWS RAID 135065 (toddb): Use of MulDiv should simplify this code
//
void CalcAspectScaledRect(const SIZE *prgSize, RECT *pRect) { ASSERT(pRect->left == 0); ASSERT(pRect->top == 0);
int iWidth = pRect->right; int iHeight = pRect->bottom; int iXRatio = (iWidth * 1000) / prgSize->cx; int iYRatio = (iHeight * 1000) / prgSize->cy;
if (iXRatio > iYRatio) { pRect->right = prgSize->cx; // work out the blank space and split it evenly between the top and the bottom...
int iNewHeight = ((iHeight * 1000) / iXRatio); if (iNewHeight == 0) { iNewHeight = 1; } int iRemainder = prgSize->cy - iNewHeight;
pRect->top = iRemainder / 2; pRect->bottom = iNewHeight + pRect->top; } else { pRect->bottom = prgSize->cy;
// work out the blank space and split it evenly between the left and the right...
int iNewWidth = ((iWidth * 1000) / iYRatio); if (iNewWidth == 0) { iNewWidth = 1; } int iRemainder = prgSize->cx - iNewWidth; pRect->left = iRemainder / 2; pRect->right = iNewWidth + pRect->left; } } void CalculateAspectRatio(const SIZE *prgSize, RECT *pRect) { int iHeight = abs(pRect->bottom - pRect->top); int iWidth = abs(pRect->right - pRect->left);
// check if the initial bitmap is larger than the size of the thumbnail.
if (iWidth > prgSize->cx || iHeight > prgSize->cy) { pRect->left = 0; pRect->top = 0; pRect->right = iWidth; pRect->bottom = iHeight;
CalcAspectScaledRect(prgSize, pRect); } else { // if the bitmap was smaller than the thumbnail, just center it.
pRect->left = (prgSize->cx - iWidth) / 2; pRect->top = (prgSize->cy- iHeight) / 2; pRect->right = pRect->left + iWidth; pRect->bottom = pRect->top + iHeight; } }
LPBYTE g_pbCMAP = NULL;
STDAPI_(BOOL) FactorAspectRatio(BITMAPINFO *pbiScaled, void *pScaledBits, const SIZE *prgSize, RECT rect, DWORD dwClrDepth, HPALETTE hpal, BOOL fOrigSize, COLORREF clrBk, HBITMAP *phBmpThumbnail) { HDC hdc = CreateCompatibleDC(NULL); BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)pbiScaled; BOOL bRetVal = FALSE; int iRetVal = GDI_ERROR; BITMAPINFO * pDitheredInfo = NULL; void * pDitheredBits = NULL; HBITMAP hbmpDithered = NULL; if (hdc) { if (dwClrDepth == 8) { RGBQUAD *pSrcColors = NULL; LONG nSrcPitch = pbiScaled->bmiHeader.biWidth; // we are going to 8 bits per pixel, we had better dither everything
// to the same palette.
GUID guidType = CLSID_NULL; switch(pbiScaled->bmiHeader.biBitCount) { case 32: guidType = BFID_RGB_32; nSrcPitch *= sizeof(DWORD); break; case 24: guidType = BFID_RGB_24; nSrcPitch *= 3; break; case 16: // default is 555
guidType = BFID_RGB_555; // 5-6-5 bitfields has the second DWORD (the green component) as 0x7e00
if (pbiScaled->bmiHeader.biCompression == BI_BITFIELDS && pbiScaled->bmiColors[1].rgbGreen == 0x7E) { guidType = BFID_RGB_565; } nSrcPitch *= sizeof(WORD); break; case 8: guidType = BFID_RGB_8; pSrcColors = pbiScaled->bmiColors; // nSrcPitch is already in bytes...
break; }; if (nSrcPitch % 4) { // round up to the nearest DWORD...
nSrcPitch = nSrcPitch + 4 - (nSrcPitch %4); } // we are going to 8bpp
LONG nDestPitch = pbiScaled->bmiHeader.biWidth; if (nDestPitch % 4) { // round up to the nearest DWORD...
nDestPitch = nDestPitch + 4 - (nDestPitch % 4); } if (guidType != CLSID_NULL) { if (g_pbCMAP == NULL) { // we are always going to the shell halftone palette right now, otherwise
// computing this inverse colour map consumes a lot of time (approx 2 seconds on
// a p200)
if (FAILED(SHGetInverseCMAP((BYTE *)&g_pbCMAP, sizeof(g_pbCMAP)))) { return FALSE; } } SIZE rgDithered = {pbiScaled->bmiHeader.biWidth, pbiScaled->bmiHeader.biHeight}; if (rgDithered.cy < 0) { // invert it
rgDithered.cy = -rgDithered.cy; } if (CreateSizedDIBSECTION(&rgDithered, dwClrDepth, hpal, NULL, &hbmpDithered, &pDitheredInfo, &pDitheredBits)) { ASSERT(pDitheredInfo && pDitheredBits); // dither....
IIntDitherer *pDither; HRESULT hr = CoCreateInstance(CLSID_IntDitherer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IIntDitherer, &pDither)); if (SUCCEEDED(hr)) { hr = pDither->DitherTo8bpp((LPBYTE) pDitheredBits, nDestPitch, (LPBYTE) pScaledBits, nSrcPitch, guidType, pDitheredInfo->bmiColors, pSrcColors, g_pbCMAP, 0, 0, rgDithered.cx, rgDithered.cy, -1, -1); pDither->Release(); } if (SUCCEEDED(hr)) { // if the height was inverted, then invert it in the destination bitmap
if (rgDithered.cy != pbiScaled->bmiHeader.biHeight) { pDitheredInfo->bmiHeader.biHeight = - rgDithered.cy; } // switch to the new image .....
pbiScaled = pDitheredInfo; pScaledBits = pDitheredBits; } } } } // create thumbnail bitmap and copy image into it.
if (CreateSizedDIBSECTION(prgSize, dwClrDepth, hpal, NULL, phBmpThumbnail, NULL, NULL)) { HBITMAP hBmpOld = (HBITMAP) SelectObject(hdc, *phBmpThumbnail); SetStretchBltMode(hdc, COLORONCOLOR); HGDIOBJ hBrush = CreateSolidBrush(clrBk); HGDIOBJ hPen = GetStockObject(WHITE_PEN); HGDIOBJ hOldBrush = SelectObject(hdc, hBrush); HGDIOBJ hOldPen = SelectObject(hdc, hPen); HPALETTE hpalOld; if (hpal) { hpalOld = SelectPalette(hdc, hpal, TRUE); RealizePalette(hdc); } SetMapMode(hdc, MM_TEXT); Rectangle(hdc, 0, 0, prgSize->cx, prgSize->cy); int iDstHt = rect.bottom - rect.top; int iDstTop = rect.top, iSrcTop = 0; if (pbih->biHeight < 0) { iDstHt *= -1; iDstTop = rect.bottom; iSrcTop = abs(pbih->biHeight); } iRetVal = StretchDIBits(hdc, rect.left, iDstTop, rect.right - rect.left, iDstHt, 0, iSrcTop, pbih->biWidth, pbih->biHeight, pScaledBits, pbiScaled, DIB_RGB_COLORS, SRCCOPY); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); SelectObject(hdc, hOldPen); if (hpal) { SelectPalette(hdc, hpalOld, TRUE); RealizePalette(hdc); } SelectObject(hdc, hBmpOld); } DeleteDC(hdc); } if (hbmpDithered) { DeleteObject(hbmpDithered); } if (pDitheredInfo) { LocalFree(pDitheredInfo); } return (iRetVal != GDI_ERROR); }
STDAPI_(BOOL) CreateSizedDIBSECTION(const SIZE *prgSize, DWORD dwClrDepth, HPALETTE hpal, const BITMAPINFO *pCurInfo, HBITMAP *phBmp, BITMAPINFO **ppBMI, void **ppBits) { *phBmp = NULL; HDC hdc = GetDC(NULL); if (hdc) { HDC hdcBmp = CreateCompatibleDC(hdc); if (hdcBmp) { struct { BITMAPINFOHEADER bi; DWORD ct[256]; } dib;
dib.bi.biSize = sizeof(dib.bi); dib.bi.biWidth = prgSize->cx; dib.bi.biHeight = prgSize->cy; dib.bi.biPlanes = 1; dib.bi.biBitCount = (WORD) dwClrDepth; dib.bi.biCompression = BI_RGB; dib.bi.biSizeImage = CalcImageSize(prgSize, dwClrDepth); dib.bi.biXPelsPerMeter = 0; dib.bi.biYPelsPerMeter = 0; dib.bi.biClrUsed = (dwClrDepth <= 8) ? (1 << dwClrDepth) : 0; dib.bi.biClrImportant = 0;
HPALETTE hpalOld = NULL; BOOL bDIB = TRUE; if (dwClrDepth <= 8) { // if they passed us the old structure with colour info, and we are the same bit depth, then copy it...
if (pCurInfo && pCurInfo->bmiHeader.biBitCount == dwClrDepth) { // use the passed in colour info to generate the DIBSECTION
int iColours = pCurInfo->bmiHeader.biClrUsed;
if (!iColours) { iColours = dib.bi.biClrUsed; }
// copy the data accross...
if (iColours <= 256) { CopyMemory(dib.ct, pCurInfo->bmiColors, sizeof(RGBQUAD) * iColours); } else { bDIB = FALSE; } } else { // need to get the right palette....
hpalOld = SelectPalette(hdcBmp, hpal, TRUE); RealizePalette(hdcBmp); int n = GetPaletteEntries(hpal, 0, 256, (LPPALETTEENTRY)&dib.ct[0]);
ASSERT(n >= (int) dib.bi.biClrUsed);
// now convert the PALETTEENTRY to RGBQUAD
for (int i = 0; i < (int)dib.bi.biClrUsed; i ++) { dib.ct[i] = RGB(GetBValue(dib.ct[i]),GetGValue(dib.ct[i]),GetRValue(dib.ct[i])); } } }
if (bDIB) { void *pbits; *phBmp = CreateDIBSection(hdcBmp, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &pbits, NULL, 0); if (*phBmp) { if (ppBMI) { *ppBMI = (BITMAPINFO *)LocalAlloc(LPTR, sizeof(dib)); if (*ppBMI) { CopyMemory(*ppBMI, &dib, sizeof(dib)); } } if (ppBits) { *ppBits = pbits; } } } DeleteDC(hdcBmp); } ReleaseDC(NULL, hdc); } return (*phBmp != NULL); }
STDAPI_(void *) CalcBitsOffsetInDIB(BITMAPINFO *pBMI) { int ncolors = pBMI->bmiHeader.biClrUsed; if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8) ncolors = 1 << pBMI->bmiHeader.biBitCount; if (pBMI->bmiHeader.biBitCount == 16 || pBMI->bmiHeader.biBitCount == 32) { if (pBMI->bmiHeader.biCompression == BI_BITFIELDS) { ncolors = 3; } } return (void *)((UCHAR *)&pBMI->bmiColors[0] + ncolors * sizeof(RGBQUAD)); }
|