翻转位图后正确释放资源

Posted

技术标签:

【中文标题】翻转位图后正确释放资源【英文标题】:Correctly release resources after flipping a bitmap 【发布时间】:2013-01-24 06:58:26 【问题描述】:

我有一个将非常大的数据集写入位图的应用程序。 [~500MB] 我正在从上到下明智地编写数据补丁。由于 BMP 文件结构的性质,一旦写入磁盘就必须翻转。 (我这样写是因为我认为翻转位图将是一个常见的应用程序,我会找到库来完成这项任务)

我正在使用我在互联网上找到的代码 sn-p 来翻转位图。这个:

// GetInvertedBitmap    - Creates a new bitmap with the inverted image
// Returns      - Handle to a new bitmap with inverted image
// hBitmap      - Bitmap to invert
// bLateral     - Flag to indicate whether to invert laterally or vertically

HBITMAP CRisatImport::GetInvertedBitmap( HBITMAP hBitmap, BOOL bLateral )

    // Create a memory DC compatible with the display
    CDC sourceDC, destDC;
    sourceDC.CreateCompatibleDC( NULL );
    destDC.CreateCompatibleDC( NULL );
    // Get logical coordinates
    BITMAP bm;
    ::GetObject( hBitmap, sizeof( bm ), &bm );
    // Create a bitmap to hold the result
    HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), 
                        bm.bmWidth, bm.bmHeight);
    // Select bitmaps into the DCs
    HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
    HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );
    if( bLateral )
        destDC.StretchBlt( 0, 0, bm.bmWidth, bm.bmHeight, &sourceDC, 
                bm.bmWidth-1, 0, -bm.bmWidth, bm.bmHeight, SRCCOPY );
    else
        destDC.StretchBlt( 0, 0, bm.bmWidth, bm.bmHeight, &sourceDC, 
                0, bm.bmHeight-1, bm.bmWidth, -bm.bmHeight, SRCCOPY );
    // Reselect the old bitmaps
    ::SelectObject( sourceDC.m_hDC, hbmOldSource );
    ::SelectObject( destDC.m_hDC, hbmOldDest );
    return hbmResult;

问题是我对上述代码的理解有限。我试图从 MSDN 的示例代码中尽可能地编写一个函数来使用上面的 sn-p。我认为我没有正确释放所有资源。而且我似乎无法弄清楚错误 - 主要是因为我对 GDI 缺乏了解。如果有人指出我做错了什么,我将不胜感激。

如果我尝试调用这个函数两次,程序就会崩溃——这就是为什么我怀疑我错误地释放了资源。

这是我写的函数:

void CRisatImport::flipBitMapSaveAsJPG( CString outputFileName, CString outputJPGName, bool saveAsJpg)

    // Flip the bitmap to correct odd file structure
        HBITMAP hBitmap;
        hBitmap = (HBITMAP)::LoadImage(NULL, outputFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        hBitmap = GetInvertedBitmap( hBitmap, FALSE );
        CImage image;
        image.Attach(hBitmap);

        image.Save(outputFileName,Gdiplus::ImageFormatBMP);
        if(saveAsJpg)
        image.Save(outputJPGName,Gdiplus::ImageFormatJPEG);
        

             image.Destroy();

        DeleteObject( hBitmap );

我正在为这个应用程序使用 MFC 和 VS2010 - 我有 4GB 的 RAM 并且没有其他应用程序正在运行。

这是我得到的错误:

【问题讨论】:

我无法测试它,但 GetInvertedBitmap 的结果应该放在另一个位图变量中(最后也应该删除)。实际上,您的代码中有 2 个位图。此外,由于您正在执行图像销毁,这将破坏附加的位图。 代码中的哪个函数触发了断言? Image.save() 我认为会触发它。 您可能有一次分配 500 兆字节的连续内存块,但您不会有两次。虚拟内存地址空间碎片不会留下足够大的漏洞。否则与RAM没有任何关系。如今,广泛使用的 64 位操作系统不再是问题。 @HansPassant - 我同意。我将不得不找到明智地翻转位图补丁的方法。你有什么策略吗? 【参考方案1】:

当您将位图句柄附加到 CImage 时,您将句柄生命周期的责任移交给 CImage,因此您不想在之后破坏它。但是您也为新位图使用了相同的变量名。试试这个:

void CRisatImport::flipBitMapSaveAsJPG( CString outputFileName, CString outputJPGName, bool saveAsJpg)

    // Flip the bitmap to correct odd file structure
        HBITMAP hBitmapLoad = (HBITMAP)::LoadImage(NULL, outputFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        HBITMAP hBitmapInverted = GetInvertedBitmap( hBitmapLoad , FALSE );
        DeleteObject( hBitmapLoad );

        CImage image;
        image.Attach(hBitmapInverted );

        image.Save(outputFileName,Gdiplus::ImageFormatBMP);
        if(saveAsJpg)
            image.Save(outputJPGName,Gdiplus::ImageFormatJPEG);
        

        image.Destroy();



【讨论】:

这解决了资源问题。但事实是系统无法同时分配 500MB。它就像小图像的魅力。我必须找到一些以补丁方式翻转位图的方法。

以上是关于翻转位图后正确释放资源的主要内容,如果未能解决你的问题,请参考以下文章

如何在android中使用imageloader释放位图内存?

已释放对象的校验和不正确 - 对象在被释放后可能已被修改。我该如何解决?

CocosCreator教程(入门篇)

AWS Lambda 中的资源释放

java在循环语句的执行语句赋予变量值循环结束后会释放吗?

Bitmap使用详解