windows-nt/Source/XPSP1/NT/shell/ext/shimgvw/thumbutil.cpp
2020-09-26 16:20:57 +08:00

1035 lines
30 KiB
C++

#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;
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...
CopyMemory(dib.ct, pCurInfo->bmiColors, sizeof(RGBQUAD) * iColours);
}
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]));
}
}
}
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));
}