上一节我们说了GDI+的矩阵,关于矩阵也只是单独的说了一下,如果以后有时间看看能否推出GDI+的应用课程。
这一节呢,我们借助GDI+打造一个简单的windows功能图片查看器,包括图片的旋转,缩放和Alpha透明处理,保存,当然还有一项新功能,显示拖拽图像文件
#pragma once
typedef struct tagSize
{
float cx;
float cy;
} _SIZE, *_PSIZE, *_LPSIZE;
class CGdiPlusImage
{
//变量定义
protected:
Image * m_pImage; //图片对象
tagSize m_szZoom; //缩放变量
int m_nRotateAngle; //旋转角度
BYTE m_cbAlphaDepth; //Alpha数值
//函数定义
public:
//构造函数
CGdiPlusImage();
//析构函数
virtual ~CGdiPlusImage();
//状态函数
public:
//是否加载
bool IsNull();
//获取宽度
INT GetWidth();
//获取高度
INT GetHeight();
//管理函数
public:
//销毁图片
bool DestroyImage();
//加载图片
bool LoadImage(LPCTSTR pszFileName);
//功能函数
public:
//设置旋转
void SetRotation(int nAngle);
//设置缩放
void SetScale(float cx,float cy);
//设置Alpha
void SetAlpha(BYTE cbAlphaDepth);
//保存图像
bool Save(LPCTSTR pszFileName,LPCTSTR format=TEXT("image/png"));
//获取解码器
int GetEncoderClsid(LPCTSTR format, CLSID* pClsid);
//绘画函数
public:
//绘画图像
bool DrawImage(CDC * pDC, INT nXPos, INT nYPos);
};
#include "StdAfx.h"
#include "GdiPlusImage.h"
//构造函数
CGdiPlusImage::CGdiPlusImage(void)
{
//设置变量
m_pImage=NULL;
m_szZoom.cx = m_szZoom.cy = 1.0f;
m_nRotateAngle = 0;
m_cbAlphaDepth = 255;
}
//析构函数
CGdiPlusImage::~CGdiPlusImage(void)
{
//销毁图片
DestroyImage();
}
//是否加载
bool CGdiPlusImage::IsNull()
{
//加载判断
if (m_pImage==NULL) return true;
if (m_pImage->GetLastStatus()!=Ok) return true;
return false;
}
//获取宽度
INT CGdiPlusImage::GetWidth()
{
//加载判断
ASSERT(IsNull()==false);
if (IsNull()==true) return 0;
//获取宽度
return m_pImage->GetWidth();
}
//获取高度
INT CGdiPlusImage::GetHeight()
{
//加载判断
ASSERT(IsNull()==false);
if (IsNull()==true) return 0;
//获取高度
return m_pImage->GetHeight();
}
//销毁图片
bool CGdiPlusImage::DestroyImage()
{
//删除对象
if (m_pImage!=NULL) SafeDelete(m_pImage);
return true;
}
//加载图片
bool CGdiPlusImage::LoadImage(LPCTSTR pszFileName)
{
//加载判断
ASSERT(m_pImage==NULL);
if (m_pImage!=NULL) return false;
//加载文件
CT2CW strFileName(pszFileName);
m_pImage=Image::FromFile((LPCWSTR)strFileName);
//错误判断
if ((m_pImage==NULL)||(m_pImage->GetLastStatus()!=Ok))
{
DestroyImage();
return false;
}
return true;
}
//绘画图像
bool CGdiPlusImage::DrawImage(CDC * pDC, INT nXPos, INT nYPos)
{
//加载判断
ASSERT(m_pImage!=NULL);
if (m_pImage==NULL) return false;
//创建屏幕
ASSERT(pDC!=NULL);
Graphics graphics(pDC->GetSafeHdc());
GraphicsContainer Containter = graphics.BeginContainer();
graphics.SetSmoothingMode(SmoothingModeHighQuality);
//获取属性
INT nImageWidth=m_pImage->GetWidth();
INT nImageHeight=m_pImage->GetHeight();
//构造位置
RectF rcDrawRect;
rcDrawRect.X=0;//(REAL)nXPos;
rcDrawRect.Y=0;//(REAL)nYPos;
rcDrawRect.Width=(REAL)nImageWidth;
rcDrawRect.Height=(REAL)nImageHeight;
Matrix matrix;
CPoint pt((nXPos+nImageWidth/2), (nYPos+nImageHeight/2));
PointF ptBase((float)pt.x,(float)pt.y);
//矩阵变换要注意顺序,先平移在缩放后旋转
matrix.Translate((REAL)nXPos,(REAL)nYPos,MatrixOrderAppend);
//缩放
if ( m_szZoom.cx != 1.0f || m_szZoom.cy != 1.0f )
{
ptBase.X += m_szZoom.cx;
ptBase.Y += m_szZoom.cy;
matrix.Translate(-ptBase.X,-ptBase.Y,MatrixOrderAppend);
matrix.Scale(m_szZoom.cx,m_szZoom.cy,MatrixOrderAppend);
matrix.Translate(ptBase.X,ptBase.Y,MatrixOrderAppend);
}
//旋转
if (m_nRotateAngle)
{
matrix.RotateAt((REAL)m_nRotateAngle,ptBase,MatrixOrderAppend);
}
graphics.SetTransform(&matrix);
//透明矩阵
ColorMatrix Matrix=
{
1.0f,0.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,0.0f,m_cbAlphaDepth/255.0f,0.0f,
0.0f,0.0f,0.0f,0.0f,1.0f
};
//设置属性
ImageAttributes Attributes;
Attributes.SetColorMatrix(&Matrix,ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);
//绘画图像
graphics.DrawImage(m_pImage,rcDrawRect,0,0,(REAL)nImageWidth,(REAL)nImageHeight,UnitPixel,&Attributes);
graphics.ResetTransform();
graphics.EndContainer(Containter);
return true;
}
void CGdiPlusImage::SetRotation( int nAngle)
{
m_nRotateAngle = nAngle;
}
void CGdiPlusImage::SetScale(float cx,float cy)
{
m_szZoom.cx = cx;
m_szZoom.cy = cy;
}
void CGdiPlusImage::SetAlpha( BYTE cbAlphaDepth )
{
if ( cbAlphaDepth < 0 ) m_cbAlphaDepth = 0;
if ( cbAlphaDepth > 255 ) m_cbAlphaDepth = 255;
m_cbAlphaDepth = cbAlphaDepth;
}
int CGdiPlusImage::GetEncoderClsid(LPCTSTR format, CLSID* pClsid)
{
UINT num = 0;
UINT size = 0;
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1;
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1;
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( StrCmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
bool CGdiPlusImage::Save(LPCTSTR pszFileName,LPCTSTR format)
{
//加载文件
CT2CW strFileName(pszFileName);
//图像保存
CLSID Clsid;
GetEncoderClsid(format, &Clsid);
if( m_pImage->Save((LPCWSTR)strFileName, &Clsid) == Ok )
return true;
return false;
}
这个类虽然短小,但是五脏俱全,在DrawImage方法中,我们在内部实现了矩阵Matrix,在这里我们需要注意一点,在做矩阵变换的时候,切记要注意顺序,先平移在缩放后旋转,否则会引发意外的效果。其次还需要注意,图片的旋转是依附于旋转中心点进行旋转的,在这里我们指定的是图片的中心点,再次,关于缩放,默认情况下,缩放会相对与左上角的位置进行平移,即,图像放大时,图像会与左上角的坐标会越来越远,反之亦然,所以,为了这种情况的发生,我们缩放后平移的位置减去,使图像一直保持窗口的中央。
关于颜色矩阵,大家可以先看一下这些文章,帮助理解
《GDI+ ColorMatrix的完全揭秘》
《ColorMatrix的含义》
《颜色调整矩阵ColorMatrix详解》
关于保存,GDI+并没有给我们提供GetEncoderClsid函数,所以,在这里我们自行封装GDI+的解码器功能,当然还有简单的方法,使用CImage的Save方法同样可以达到保存的目的,不过,本篇主要是讲解GDI+的Image使用过程,CImage的使用就不说了。
关于从桌面等文件夹拖拽显示图像文件,主要是响应WM_DROPFILES消息,不过在添加WM_DROPFILES消息之前,先把对话框的Accept Files属性设为true,否则一切都是徒劳。
而拖拽,我们住需要2个函数就够了,DragQueryFile的返回值,返回我们往窗口中拖进多少张资源,参数用来获取拖进窗口的文件的完整路径,有了路径,剩下加载图片也就简单的不能再简单了
void CImageHandleDlg::OnDropFiles(HDROP hDropInfo)
{
UINT nCount;
TCHAR szFilePath[MAX_PATH];
nCount = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
if(nCount)
{
DragQueryFile(hDropInfo, nCount-1, szFilePath, sizeof(szFilePath));
}
DragFinish(hDropInfo);
if ( m_Image.IsNull() == false )
{
m_Image.DestroyImage();
}
if( !m_Image.LoadImage(szFilePath) )
{
AfxMessageBox(TEXT("加载资源失败!"));
return;
}
CRect rcClient;
GetClientRect(&rcClient);
//初始化变量
m_ptPos.SetPoint((rcClient.Width()-m_Image.GetWidth())/2,(rcClient.Height()-m_Image.GetHeight())/2);
m_fScale = 1.0f;
m_nAngle = 0;
//恢复数据
m_Image.SetRotation(m_nAngle);
m_Image.SetScale(m_fScale,m_fScale);
Invalidate(FALSE);
CBaseDialog::OnDropFiles(hDropInfo);
}
如上示例代码中,通过DragQueryFile我们返回出用户拖进多少个文件,根据返回的数量,我们可以挨个遍历这些文件,依次通过DragQueryFile继续把文件的路径信息全部查询出来,在这里我也只是将最后的一个文件的地址返回了出来,如果你的项目中需要处理多个文件,那么就for遍历挨个取路径吧
最后,关于保存文件框CFileDialog,CFileDialog默认情况下是不会给文件追加后缀名的,除非我们手工输入后缀名,不过,我们可以通过获取CFileDialog下方的类型来追加后缀名,(ps:这里很多朋友之前都遇到过这种问题,这里大家也留心一下)
CString strFileName;
CString strExtension;
strFileName = dlg.m_ofn.lpstrFile;
switch (dlg.m_ofn.nFilterIndex)
{
case 1:
strExtension = TEXT("bmp"); break;
case 2:
strExtension = TEXT("jpeg"); break;
case 3:
strExtension = TEXT("gif"); break;
case 4:
strExtension = TEXT("png"); break;
default:
break;
}
strFileName = strFileName + TEXT('.') + strExtension;
好了,总的情况就这些了,更多的图片处理和特效,大家可以继续扩展
源码下载地址:
http://pan.baidu.com/s/11QpOi