将位图缩小到所需的尺寸

时间:2009-06-09 11:55:38

标签: c++ winapi visual-c++ mfc bitmap

我有一个大尺寸的位图(2000 x 2000)我需要将该位图缩小到一个小尺寸(150 x 150)。我已经为它编写了一个代码,但它没有用。任何人都可以帮助找到问题吗?问题是目标位图只是空白。我选错了DC?我确保源和目的地都是正确的。在做bitblt后,我还需要对目标位图做更多的事情吗?

BOOL ReSizeBitmap(CBitmap *pBitmap, CBitmap *pNewBitmap)
{

    // Get new bitmap size
    BITMAP bmOld;
    if( !pBitmap->GetBitmap(&bmOld) )
    {
        return FALSE;
    }

    CRect rcPrev(0, 0, bmOld.bmWidth, bmOld.bmHeight);
    int newWidth  = 150;
    int newHeight = 150;

    if( newWidth < 1 || newHeight < 1 )
    {
        ::SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    BOOL bResult = FALSE;
    try
    {   
            CDC dcDest;
            CDC dcSource;

            dcSource.CreateCompatibleDC(NULL);
            dcDest.CreateCompatibleDC(NULL);

            CBitmap* pSourceOld = dcSource.SelectObject(pBitmap);
            CBitmap* pDestold = dcDest.SelectObject(pNewBitmap);

            if( !pNewBitmap->CreateCompatibleBitmap(
                &dcDest, newWidth, newHeight) )
            {

                return FALSE;
            }

            int oldStretchMode = dcDest.SetStretchBltMode(HALFTONE);

            bResult = dcDest.StretchBlt(
                0, 0, 150, 150,
                &dcSource, 0, 0, bmOld.bmWidth, bmOld.bmHeight,
                SRCCOPY);   
            dcDest.SetStretchBltMode(oldStretchMode);

            dcSource.SelectObject(pSourceOld);
            dcDest.SelectObject(pDestold);


        bResult = TRUE;
    }
    catch(CResourceException* /*e*/)
    {

    }

    return bResult;
}

5 个答案:

答案 0 :(得分:1)

即使代码有效,也可以做一些清理工作 当你在MFC工作时,RAII是你真正需要的成语之一!

if( !pNewBitmap->CreateCompatibleBitmap(&dcDest, newWidth, newHeight) )
{
   return FALSE;
}

当您返回FALSE或存在未调用

的异常时
cSource.SelectObject(pSourceOld);
dcDest.SelectObject(pDestold); 

在离开功能之前进行清理。

创建一个小助手类来一直清理,你不必担心return或throw语句。

class SelectObjectAndCleanUp
{
    CDC& deviceContext;
    CBitmap *const oldSource;
public:
    SelectObjectCleanUp( CDC& deviceContext, CBitmap* source ) 
    : deviceContext(deviceContext),
      oldSource( deviceContext.SelectObject(source) ) {
    }

    ~SelectObjectCleanUp() {
            deviceContext.SelectObject(oldSource) 
    }
};

// use of the helper
SelectObjectCleanUp  sourceSelectionAndCleanup(dcSource, pBitmap );
SelectObjectCleanUp  destionationSelectionAndCleanup(dcDest, pNewBitmap );

答案 1 :(得分:1)

我对C ++不是很熟悉,但是你在创建新DC之前是否选择了新的位图?此外,当您调用CreateCompatibleBitmap时,我认为您希望使用屏幕DC(用于创建目标DC的屏幕),而不是兼容的内存DC。因此,使用GetDC获取屏幕DC,并将其传递到CreateCompatibleDC和CreateCompatibleBitmap。

答案 2 :(得分:0)

有一个很棒的免费C ++图像库,名为CxImage,在zlib许可下是开源的。

无需重新发明轮子。

答案 3 :(得分:0)

  • 下载http://www.gdiwatch.com/,它可能会显示错误的位置(也可以将其与vs 2008一起使用 - 只需将注册表项从vs2005手动复制到vs2008目录)

  • 您是否尝试在SelectObject()调用之前执行CreateCompatibleBitmap()?

  • 新位图的GetBitmap()是否返回正确的大小,即新的位图是否有效?

其余的似乎没问题,它应该像我相信的那样工作。它可以与其他StretchBltModes一起使用吗?

答案 4 :(得分:-1)

感谢所有偷看并提出解决方案的人,在经过一些调试之后我发现了问题。这是解决方案!!!

CBitmap *SrcBmp;
HBITMAP hBmp;
hBmp= (HBITMAP)LoadImage( NULL, L"c:\\source.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE );
SrcBmp = CBitmap::FromHandle(hBmp);

BITMAP BmpInfo;
SrcBmp->GetBitmap(&BmpInfo);


CDC SrcDC;
SrcDC.CreateCompatibleDC(NULL);

CBitmap DestBmp;
DestBmp.CreateCompatibleBitmap(&SrcDC,150,150);

CDC DestDC;
DestDC.CreateCompatibleDC(NULL);

CBitmap *pOldBmp1 = SrcDC.SelectObject(SrcBmp);
CBitmap *pOldBmp2 = DestDC.SelectObject(&DestBmp);

DestDC.StretchBlt(0,0,150,150,&SrcDC,0,0,BmpInfo.bmWidth,BmpInfo.bmHeight,SRCCOPY);

CImage image;
image.Attach(DestBmp);
image.Save(_T("C:\\test.bmp"), Gdiplus::ImageFormatBMP);

SrcDC.SelectObject(pOldBmp1);
DestDC.SelectObject(pOldBmp2);