65.9K
CodeProject 正在变化。 阅读更多。
Home

CDibData

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (19投票s)

2003 年 12 月 23 日

4分钟阅读

viewsIcon

183006

downloadIcon

7194

CDibData 是一个用于加载、保存和操作位图的实用类

引言

CDibData 是一个用于简化加载、保存、转换(颜色深度)和访问位图图像的类。它最初被设计为一个实用类,用于直接访问位图对象的图像数据。意识到它本身就很有用后,我决定扩展它的功能,包括:位图的加载、保存和转换。该类不需要将位图加载为 DIBSECTION 才能使用,而这在它被创建时是原始要求。

我还附带了 Jeff ProsiseQuantize.cpp 副本;MFC 库中可以找到 Quantize.cpp,有兴趣的读者可以阅读。

好的,在阅读完这篇文章并测试完代码后:请指正我的错误,找出 bug,告诉我如何改进代码和/或 Doxygen 生成的文档。尽你所能来破坏代码,即使是你们这些质量控制专家(擅长荒谬的事情)。如果有什么不对,我希望知道!

背景

开发此代码的主要参考资料是 Charles Petzold 的《Programming Windows Fifth Edition》和 MFC 库。大部分参考资料都列在 CDibData.cpp 中。

Using the Code

DIB 被内部存储在一个全局分配的内存块中,适用于通过剪贴板使用 CF_DIB 格式进行传输。由于 CDibData 不是 CBitmap 的替代品,因此它不维护原始位图句柄的副本。

如果您没有安装 Microsoft 的 Windows SDK,在调试模式下编译时会遇到一些编译错误。原因是 Visual C++ 6.0 附带的 SDK 中没有定义 BITMAPV5HEADER。由于 BITMAPV5HEADER 仅用于显示调试信息,因此注释掉相关代码应该很容易。

注意:当 CDibData 对象包含压缩的 DIB 时,您将无法直接访问图像数据。因此,您可以将 DIB 图像保存到文件,但不能转换它或使用任何需要直接访问图像数据的函数。

如何加载位图文件的示例

// Loading file using CDibData::LoadDIB()
// This method also allows for the loading of 2 bits-per-pixels bitmaps by
// converting them to 4 bits-per-pixels when loading.
HBITMAP hBitmap = m_DibData.LoadDIB(lpszPathName);
if( !hBitmap )
    return FALSE;
    
m_Bitmap.Attach(hBitmap);
// Loading file using LoadImage()
HBITMAP hBitmap = (HBITMAP)::LoadImage(
    NULL, lpszPathName, IMAGE_BITMAP,
    0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
if( !hBitmap )
    return FALSE;
    
if( m_Bitmap.Attach(hBitmap) )
    m_DibData.CreateDIB(&m_Bitmap);
// Loading file directly into a CDibData object
// Note: DibXXXX() functions are actually just defined wrappers for
// GlobalXXXX() functions (used internally for debugging).
try
{
    // Copy DIB from file
    CFile cf(lpszPathName, CFile::modeRead);

    // Read bitmap file header
    BITMAPFILEHEADER bmfh;
    cf.Read((void*)&bmfh, sizeof(BITMAPFILEHEADER));

    // Verify file type
    if( bmfh.bfType != 0x4D42 )
    {
        cf.Close();
        return NULL;
    }

    // Read DIB from file
    DWORD dwLen = cf.GetLength() - sizeof(BITMAPFILEHEADER);
    hDib = DibAlloc(dwLen);
    pDib = DibLock(hDib);
    if( pDib )
        cf.Read((void*)pDib, (UINT)dwLen);
    cf.Close();

    if( !pDib )
    {
        if( hDib )
            DibFree(hDib);
        return NULL;
    }

    DibUnlock(hDib);
}
catch( CFileException* /*e*/)
{
    if( pDib )
        DibUnlock(hDib);
    if( hDib )
        DibFree(hDib);
    return NULL;
}

// Attach DIB to this object
if( !Attach(hDib) )
{
    DibFree(hDib);
    return NULL;
}

如何保存位图文件的示例

// Save file using internally stored DIB data.
m_DibData.CreateDIB.SaveDIB(lpszPathName);
// Save compressed file using internally stored DIB data.
m_DibData.CreateDIB.SaveDIB(lpszPathName, NULL, TRUE);
// Save file using external bitmap source.
m_DibData.CreateDIB.SaveDIB(lpszPathName, &m_Bitmap);
// Save compressed file using external bitmap source.
m_DibData.CreateDIB.SaveDIB(lpszPathName, &m_Bitmap, TRUE);

从一种格式转换为另一种格式的示例

// Convert internally stored DIB data to new format
// Note: Use delete to destroy pDibClass when finished with object
CDibData* pDibClass = m_DibData.GetConvertedDIB(wNewColorDepth);
// Convert internally stored DIB data to new format using palette optimization
CDibData* pDibClass = m_DibData.GetConvertedDIB(wNewColorDepth, TRUE);
// Create converted CDibData object from CBitmap object
CDibData ddObj;
ddObj.CreateDIB(&m_Bitmap);
CDibData* pDibClass = ddObj.GetConvertedDIB(wNewColorDepth, TRUE);

90度旋转示例

// Create source DIB
CDibData dibSrc;
if( !dibSrc.CreateDIB(&bmpSrc) )
    return FALSE;
    
int x, y, t;
int cx = dibSrc.GetWidth();
int cy = dibSrc.GetHeight();

BOOL bResult = FALSE;

// Create rotated destination bitmap bits object
CBitmap bmpDest;
{
    CDC dcDest;
    dcDest.CreateCompatableDC(NULL);
    CBitmap* pOldBitmap = dcDest.SelectObject(&bmpSrc);
    BOOL bRet = bmpDest.CreateCompatibleBitmap(&dcDest, cy, cx);
    dcDest.SelectObject(pOldBitmap);
    dcDest.DeleteDC();
    if( !bRet )
        return bRet;
}

CDibData dibDest;
if( !dibDest.CreateDIB(&bmpDest) )
    return FALSE;

// Rotate right (90 degrees)
if( nRotations == 0 )
{
    for( x = 0; x < cx; ++x )
    {
        t = cy - 1;
        for( y = 0; y < cy; ++y, --t )
            dibDest.CopyPixelValue( t, x, dibSrc, x, y);
    }
}
// Rotate left (90 degrees)
else
{
    for( y = 0; y < cy; ++y )
    {
        t = cx - 1;
        for( x = 0; x < cx; ++x, --t )
            dibDest.CopyPixelValue(y, t, dibSrc, x, y);
    }
}

// Copy destination bits to destination bitmap
if( !dibDest.SetDIBits(&bmpDest) )
    return FALSE;

关注点

其中一些可能对您感兴趣

  • 问题:每个函数前面的注释风格是怎么回事?
  • 回答:我决定使用 Doxygen 为此对象生成文档。我以前用它来帮助我理解别人的代码,但从未用它来创建我自己的准确文档。您会在 CDibData 目录中找到 Doxygen.dat,Doxygen 向导会使用它为 CDibData 类生成文档。
  • 问题:可以使用 GetDIBits() 将位图从一种格式转换为另一种格式吗?
  • 回答:是的,但在转换为较低的颜色深度时会丢失重要的颜色信息。原因是 GetDIBits() 没有进行颜色优化。
  • 问题:为什么使用 CDibData::LoadDIB() 而不是 LoadImage()
  • 回答CDibData::LoadDIB() 可以加载自顶向下位图和 2 位/像素的位图,而 LoadImage() 不能。据我所知,WinCE 支持 2 位/像素(灰度)位图,因此我决定支持 2 位/像素位图的加载、保存和操作。尽管 CDibData 识别并可以操作 2 位/像素的位图,但为了创建 GDI 在非 WinCE 设备(即您的显示器)上可用的 HBITMAP 句柄,在加载时它必须将其转换为 4 位/像素。
  • 问题CDibData 是否支持 WinCE?
  • 回答:不支持。尽管 CDibData 支持 2 位/像素的位图操作,但它设计用于在 PC 上工作。支持 2 位/像素的主要原因是因为必须有人创建 WinCE 使用的位图。我还想指出,CDibData 不会记住加载的位图的原始格式,因此您在保存时必须将其转换回 2 位/像素。
  • 问题CDibData 是否支持最新的位图头类型?
  • 回答:不支持。CDibData 只支持具有 BITMAPINFOHEADER 的 DIB。
  • 问题:为什么不使用 GetBitmapBits() 来访问图像数据?
  • 回答:请参阅 MFC 库中的 Q131896。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.