1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > cimage和gdi绘图效率比较_使用MFC CImage类和GDI+ Image加载并绘制PNG图片

cimage和gdi绘图效率比较_使用MFC CImage类和GDI+ Image加载并绘制PNG图片

时间:2021-03-07 23:32:05

相关推荐

cimage和gdi绘图效率比较_使用MFC CImage类和GDI+ Image加载并绘制PNG图片

标签:

一、使用MFC CImage类加载PNG图片

为了测试CImage绘制PNG图片的效果,我们用截图软件截得一张360的界面,然后使用PhotoShop等工具在图片的周边加上了透明的区域,然后保存成PNG图片文件。CImage首先从文件中加载,即

CImage* m_pImgBk;

......

m_pImgBk = new CImage;

m_pImgBk->Load( _T("res\\bk.png"));

if ( m_pImgBk->IsNull() ) // 图片加载失败

{

delete m_pImgBk;

m_pImgBk = NULL;

}

然后再到测试对话框的OnPaint中绘制,即

void CTestCImageDrawDlg::OnPaint()

{

CDialogEx::OnPaint();

CWindowDC dc(this);

if ( m_pImgBk != NULL )

{

m_pImgBk->Draw(dc.GetSafeHdc(), 30, 30, m_pImgBk->GetWidth(), m_pImgBk->GetHeight() );

}

}

结果发现了下面的一些问题。

1、直接使用CImage来绘制带透明部分的PNG图片,透明区域并没有透掉(非缩放)

按照图片的原始尺寸绘制到测试对话框界面上,结果透明区域没有透掉,代码如下所示。

CWindowDC dc(this);

if ( m_pImgBk != NULL )

{

m_pImgBk->Draw(dc.GetSafeHdc(), 30, 30, m_pImgBk->GetWidth(), m_pImgBk->GetHeight() );

} 显示的效果图如下所示。

经查阅,对于带透明区域的PNG图片需要做额外的处理,判断是否启用了Alpha透明通道,若启用则要对之做如下处理:

if ( m_pImgBk->GetBPP() == 32 )

{

for(int i = 0; i < m_pImgBk->GetWidth(); i++)

{

for(int j = 0; j < m_pImgBk->GetHeight(); j++)

{

unsigned char* pucColor = reinterpret_cast(m_pImgBk->GetPixelAddress(i , j));

pucColor[0] = pucColor[0] * pucColor[3] / 255;

pucColor[1] = pucColor[1] * pucColor[3] / 255;

pucColor[2] = pucColor[2] * pucColor[3] / 255;

}

}

}

经处理该透掉的区域均被透掉,如下所示。

2、使用CImage::Draw直接绘制缩放的PNG图片时,则显示不全、失真严重

考虑到在某些情况下,要对PNG图片进行缩放,所以对缩放绘制效果进行了测试。缩放时要做到长度和宽度的等比例缩放,相关代码如下所示。

CWindowDC dc(this);

if ( m_pImgBk != NULL )

{

int nDstWidth = 450;

int nDstHeight = (int)( (nDstWidth*1.0/m_pImgBk->GetWidth())*m_pImgBk->GetHeight() ); // 宽和高等比例缩放

m_pImgBk->Draw(dc.GetSafeHdc(), 30, 30, nDstWidth, nDstHeight );

}

查阅MSDN,看是否有相关接口或参数能较好的处理这种缩放的问题。发现在CImage::Draw中可以添加Gdiplus::InterpolationMode的参数,GO过去看了一下,可以选用Gdiplus::InterpolationModeHighQuality高质量类型,发现缩放失真改善了许多,但本该透掉的透明部分却变黑了,如下所示。

所以,CImage处理带透明部分的PNG图片,特别是缩放时是有缺陷的。后来改用gdi+的Image类,则没有类似的问题。其实CImage内部也是使用gdi+实现的,具体为什么会出现上述问题尚不明确。可以直接使用gdi+的Image类来处理PNG图片。使用Image类是借助Gdiplus::Graphics来绘制的,即使用Image来加载图片,使用Gdiplus::Graphics将Image中的图片绘制到界面DC中。

二、使用GDI+ Image类加载PNG图片 1、使用Image类遇到的两个问题

(1)直接使用Image来定义对象,如下所示:

Image img;提示这样的错误:error C2248: “Gdiplus::Image::Image”: 无法访问 protected 成员(在“Gdiplus::Image”类中声明) c:\program files\microsoft sdks\windows\v7.0a\include\gdiplusheaders.h(471) : 参见“Gdiplus::Image::Image”的声明。

查看Image的声明,得知Image不带参数的构造函数是protected的,不能在外部访问的,所以不行,如下所示:

由于还有两个下面的构造函数:

所以下面的使用方法是可以的:

Image img( L"res\\bk.png" );当然这只适合局部变量,不适合定义成员变量,然后在初始化时去加载图片,供后续使用。

(2)为了在Image*成员变量指针,在初始化时new一个Image对象,并加载图片,代码如下:

m_pImgBk = new Image( L"res\\bk.png" );结果编译出现问题:error C2660: “Gdiplus::GdiplusBase::operator new”: 函数不接受 3 个参数

于是到网上搜索了一下,主要是是微软MFC的 DEBUG_NEW 和 GDI+ 不匹配造成的,有以下几个方法:

方法1:

注释掉cpp中下面的代码这就好了:

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

方法2:

::new Bitmap(cx,cy,PixelFormat32bppRGB); //加上全局作用域说明符

方法3:

详细见:

Microsoft Foundation Classes DEBUG_NEW Does Not Work with GDI+

2、使用Image静态函数来加载PNG图片

使用如下的FromFile和FromStream函数来加载,返回Image指针,正好保存在成员变量Image* m_pImgBK中。函数如下所示:

使用FromFile函数比较简单,直接通过图片路径去加载,代码如下:

m_pImgBk = Image::FromFile( L"res\\bk.png" );但是使用FromFile有个问题,会将图片文件“锁住”,其他对象将无法加载,所以还是使用FromStream,但是使用FromStream比较复杂,其大体思路为:先将图片文件数据读到内存中,然后用GlobalAlloc分配同样大的内存hGlobal,然后将图片数据拷贝到hGlobal中,然后再利用hGlobal调用CreateStreamOnHGlobal来创建IStream,组后调用Image::FromStream最终将图片加载到Image对象中。

使用Image::FromStream有两种情况,一是直接加载磁盘上的图片文件,二是加载资源中的图片文件数据。在实现时,都是先将图片文件数据读到内存中,按照上述方法操作。代码要实现这两种情况对应的接口。

(1)加载磁盘中的图片文件

Image* LoadFromFile( LPCTSTR lpszFile )

{

Image* pImage = NULL;

TCHAR achErrInfo[512] = { 0 };

HANDLE hFile = ::CreateFile( lpszFile, GENERIC_READ, FILE_SHARE_READ, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

if( hFile == INVALID_HANDLE_VALUE )

{

memset( achErrInfo, 0, sizeof(achErrInfo) );

_stprintf( achErrInfo, _T("Load (file): Error opening file %s\n"), lpszFile );

::OutputDebugString( achErrInfo );

return NULL;

}

DWORD dwSize;

dwSize = ::GetFileSize( hFile, NULL );

HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_NODISCARD, dwSize );

if ( !hGlobal )

{

::OutputDebugString( _T("Load (file): Error allocating memory\n") );

::CloseHandle( hFile );

return NULL;

};

char *pData = reinterpret_cast(GlobalLock(hGlobal));

if ( !pData )

{

::OutputDebugString( _T("Load (file): Error locking memory\n") );

GlobalFree( hGlobal );

::CloseHandle( hFile );

return NULL;

};

try

{

DWORD dwReadBytes = 0;

::ReadFile( hFile, pData, dwSize, &dwReadBytes, NULL );

}

catch( ... )

{

memset( achErrInfo, 0, sizeof(achErrInfo) );

_stprintf( achErrInfo, _T("Load (file): An exception occured while reading the file %s\n"),

lpszFile );

::OutputDebugString( achErrInfo );

GlobalFree( hGlobal );

::CloseHandle( hFile );

return NULL;

}

GlobalUnlock( hGlobal );

::CloseHandle( hFile );

IStream *pStream = NULL;

if ( CreateStreamOnHGlobal( hGlobal, TRUE, &pStream ) != S_OK )

{

return NULL;

}

pImage = Image::FromStream( pStream );

// 要加上这一句,否则由GlobalAlloc得来的hGlobal内存没有被释放,导致内存泄露,由于

// CreateStreamOnHGlobal第二个参数被设置为TRUE,所以调用pStream->Release()会自动

// 将hGlobal内存(参见msdn对CreateStreamOnHGlobal的说明)

pStream->Release();

return pImage;

}

(2)加载资源中的图片文件

Image* LoadFromRes( UINT nResID, LPCTSTR lpszResType, HINSTANCE hInstance )

{

Image* pImage = NULL;

ASSERT( lpszResType );

HRSRC hPic = FindResource( hInstance, MAKEINTRESOURCE(nResID), lpszResType );

HANDLE hResData = NULL;

if ( !hPic || !( hResData = LoadResource( hInstance,hPic ) ) )

{

::OutputDebugString( _T( "Load (resource): Error loading resource: %d\n" ) );

return NULL;

}

DWORD dwSize = SizeofResource( hInstance, hPic );

// hResData is not the real HGLOBAL (we can't lock it)

// let's make it real

HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_NODISCARD, dwSize );

if ( !hGlobal )

{

::OutputDebugString( _T("Load (resource): Error allocating memory\n" ) );

FreeResource( hResData );

return NULL;

}

char *pDest = reinterpret_cast (GlobalLock(hGlobal));

char *pSrc = reinterpret_cast (LockResource(hResData));

if ( !pSrc || !pDest )

{

::OutputDebugString( _T( "Load (resource): Error locking memory\n" ) );

GlobalFree( hGlobal );

FreeResource( hResData );

return NULL;

};

memcpy( pDest, pSrc, dwSize );

FreeResource( hResData );

GlobalUnlock( hGlobal );

IStream *pStream = NULL;

if ( CreateStreamOnHGlobal( hGlobal, TRUE, &pStream ) != S_OK )

{

return NULL;

}

pImage = Image::FromStream( pStream );

// 要加上这一句,否则由GlobalAlloc得来的hGlobal内存没有被释放,导致内存泄露,由于

// CreateStreamOnHGlobal第二个参数被设置为TRUE,所以调用pStream->Release()会自动

// 将hGlobal内存(参见msdn对CreateStreamOnHGlobal的说明)

pStream->Release();

return pImage;

}

图片加载到Image对象中后,使用GDI+中的Graphics对象绘制即可,代码如下:

Gdiplus::Graphics graphics( dc );

graphics.DrawImage( m_pImgBK, 30, 30, nDstWidth, nDstHeight )

最后需要说明的是,Image::FromFile和Image::FromStream函数返回的指针是new出来的Image对象,在使用完后要在外部将Image对象delete掉。msdn中有相关的说明,如下:

从msdn中给出的示例代码也能看的出来,返回的Image对象也是要delete的,如下:

VOID Example_FromStream(HDC hdc)

{

Graphics graphics(hdc);

Image* pImage1 = NULL;

Image* pImage2 = NULL;

IStorage* pIStorage = NULL;

IStream* pIStream1 = NULL;

IStream* pIStream2 = NULL;

HRESULT hr;

Status stat;

// Open an existing compound file, and get a pointer

// to its IStorage interface.

hr = StgOpenStorage(

L"CompoundFile.cmp",

NULL,

STGM_READ|STGM_SHARE_EXCLUSIVE,

NULL,

0,

&pIStorage);

if(FAILED(hr))

goto Exit;

// Get a pointer to the stream StreamImage1 in the compound file.

hr = pIStorage->OpenStream(

L"StreamImage1",

NULL,

STGM_READ|STGM_SHARE_EXCLUSIVE,

0,

&pIStream1);

if(FAILED(hr))

goto Exit;

// Get a pointer to the stream StreamImage2 in the compound file.

hr = pIStorage->OpenStream(

L"StreamImage2",

NULL,

STGM_READ|STGM_SHARE_EXCLUSIVE,

0,

&pIStream2);

if(FAILED(hr))

goto Exit;

// Create a new Image object based on StreamImage1.

pImage1 = Image::FromStream(pIStream1);

stat = pImage1->GetLastStatus();

if(stat != Ok)

goto Exit;

graphics.DrawImage(pImage1, 10, 10);

// Create a new Image object based on StreamImage2.

pImage2 = Image::FromStream(pIStream2);

stat = pImage2->GetLastStatus();

if(stat != Ok)

goto Exit;

graphics.DrawImage(pImage2, 200, 10);

Exit:

if(pImage1)

delete pImage1;

if(pImage2)

delete pImage2;

if(pIStream1)

pIStream1->Release();

if(pIStream2)

pIStream2->Release();

if(pIStorage)

pIStorage->Release();

}

版权声明:本文为博主原创文章,未经博主允许。

标签:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。