GDI+打造图片查看器(二)

上一节我们说了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

你可能感兴趣的:(GDI+打造图片查看器(二))