主页 > 原创 > 基于FreeImage的图片局部放大镜实现

基于FreeImage的图片局部放大镜实现

最终实现的效果如下,实际效果见文后附件:
(1)鼠标移动靠近标注图片时弹出放大镜窗口(远离时隐藏),根据鼠标位置绘制鼠标附近区域的图像

(2)点击鼠标左键时弹出放大镜窗口
(3)移动鼠标放大镜窗口更新图像为鼠标附件区域的图像
(4)鼠标不点击左键时放大镜窗口中心显示红色中空十字表示鼠标当前位置
(5)绘制的直线和曲线同步到放大镜窗口中

1.实现原理
1.通过继承CDialogEx,自绘对话框并显示非模态对话框来显示界面
2.通过FreeImagePlus显示图片,并通过鼠标位置计算局部显示的位置
3.在鼠标焦点界面的onMouseMove(鼠标移动)函数中捕获鼠标位置
4.通过gdi绘制直线(曲线为多段直线),经过坐标转换计算后显示到放大镜窗口中
5.将鼠标移动的点记录到列表中,保存曲线和直线轨迹。

2.代码实现
1.窗口显示位置

#define ZOOM_DLG_X 0
#define ZOOM_DLG_Y 0
#define ZOOM_DLG_WIDTH  300
#define ZOOM_DLG_HEIGHT 300

2.DlgImageZoom.h文件

#pragma once
#include "FreeImagePlus.h"
#include "DlgMessage.h"


// CDlgImageZoom dialog

class CDlgImageZoom : public CDialogEx
{
 DECLARE_DYNAMIC(CDlgImageZoom)

public:
 CDlgImageZoom(CWnd* pParent = NULL);   // standard constructor
  virtual ~CDlgImageZoom();

// Dialog Data
 enum { IDD = IDD_DLG_IMAGE_ZOOM };

private:
  CString m_szImagePath;
  fipWinImage m_fip;

  CDC* m_pDC;
  CRect m_rect;
  CDC m_memDC;
 CBitmap* m_pOrgBMP;
  CBitmap m_memBitmap;

  int m_imageHeight;
  int m_imageWidth;
 vector<vector<CPoint>> m_pointList;
 CPoint m_center;

public:
 void SetImagePath(CString path);
  void UpdateDraw(CPoint point, BOOL isLeftDown=FALSE, BOOL isLine=FALSE);
  int GetHeight();
  int GetWidth();
 void AddList();
 void EraseLines();

protected:
  virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

  DECLARE_MESSAGE_MAP()

 virtual BOOL OnInitDialog();
  void prepareDraw(BOOL useBack=FALSE);
 void releaseDraw();
 void drawLine(vector<CPoint>& list, CPoint &point);
 void drawCross();
public:
  afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};

3.DlgImageZoom.cpp文件

// DlgImageZoom.cpp : implementation file
//

#include "stdafx.h"
#include "Coalmine.h"
#include "DlgImageZoom.h"
#include "afxdialogex.h"

// CDlgImageZoom dialog

IMPLEMENT_DYNAMIC(CDlgImageZoom, CDialogEx)

  static bool isDrawing;

CDlgImageZoom::CDlgImageZoom(CWnd* pParent /*=NULL*/)
 : CDialogEx(CDlgImageZoom::IDD, pParent/*, NULL, en_Wnd_Normal*/)
{
  isDrawing = false;
}

CDlgImageZoom::~CDlgImageZoom()
{
}

void CDlgImageZoom::DoDataExchange(CDataExchange* pDX)
{
 CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CDlgImageZoom, CDialogEx)
 ON_WM_ERASEBKGND()
END_MESSAGE_MAP()

void CDlgImageZoom::SetImagePath( CString path )
{
  m_szImagePath = getExePath()+path;
  if (!m_fip.loadU(m_szImagePath, 0))
 {
   CDlgMessage(L"系统错误", L"加载图片失败,放大镜失效").DoModal();
  }
 else
  {
   m_imageHeight = m_fip.getHeight();
    m_imageWidth = m_fip.getWidth();
    AddList();
    GetClientRect(&m_rect);
   m_center.x = m_rect.Width()/2;
    m_center.y = m_rect.Height()/2;
 }
}

BOOL CDlgImageZoom::OnInitDialog()
{
 CDialogEx::OnInitDialog();
  return TRUE;
}

void CDlgImageZoom::prepareDraw(BOOL useBack/*=FALSE*/)
{
 m_pDC=GetDC();
  GetClientRect(&m_rect);

 m_memDC.CreateCompatibleDC(m_pDC);
  m_memBitmap.CreateCompatibleBitmap(m_pDC,m_rect.Width(),m_rect.Height());
 m_pOrgBMP = m_memDC.SelectObject(&m_memBitmap);
 CBrush brush(RGB(255,255,255));
 m_memDC.FillRect(m_rect, &brush);

 //可以尝试在对内存DC绘图之前将显示DC图像做为背景复制到内存DC中.
  if (useBack)
  {
   m_memDC.BitBlt(0,0,m_rect.Width(),m_rect.Height(),m_pDC,0,0,SRCCOPY);
 }
}

void CDlgImageZoom::releaseDraw()
{
  m_pDC->BitBlt(0,0,m_rect.Width(),m_rect.Height(),&m_memDC,0,0,SRCCOPY);
 m_memDC.SelectObject(m_pOrgBMP);
  m_memDC.DeleteDC();
 m_memBitmap.DeleteObject();
 ReleaseDC(m_pDC);
}

void CDlgImageZoom::UpdateDraw( CPoint point, BOOL isLeftDown/*=false*/, BOOL isLine/*=FALSE*/)
{
  int size = m_pointList.size();
  if (isLeftDown)
 {
   if (isLine && m_pointList[size-1].size()==2)
    {
     m_pointList[size-1][1] = point;
   }
   else
    {
     m_pointList[size-1].push_back(point);
   }
 }

 if (isDrawing)
  {
   return;
 }

 isDrawing = true;

 prepareDraw();

  CRect rect(0, 0, m_imageWidth, m_imageHeight);
 
  rect.OffsetRect(-point);
  rect.OffsetRect(m_center);
  m_fip.draw(m_memDC, rect);

  CPen penBlue(PS_SOLID, 1, RGB(0,0,255));
  CPen penGreen(PS_SOLID, 1, RGB(0,255,0));
 CPen penRed(PS_SOLID, 1, RGB(255,0,0));

 if (isLeftDown)
 {
   if (size>1)
   {
     m_memDC.SelectObject(&penBlue);
   }
   else
    {
     m_memDC.SelectObject(&penGreen);
    }

   for (int i=0;i<size-1;i++)
    {
     drawLine(m_pointList[i], -point+m_center);
    }
   m_memDC.SelectObject(&penGreen);
    drawLine(m_pointList[size-1], -point+m_center);
 }
 else
  {
   m_memDC.SelectObject(&penBlue);
   for (int i=0; i<size; i++)
    {
     drawLine(m_pointList[i], -point+m_center);
    }
   m_memDC.SelectObject(&penRed);
    drawCross();
  }

 releaseDraw();
  isDrawing = false;
}

int CDlgImageZoom::GetHeight()
{
  return m_imageHeight;
}

int CDlgImageZoom::GetWidth()
{
  return m_imageWidth;
}

void CDlgImageZoom::AddList()
{
 vector<CPoint> list;
  m_pointList.push_back(list);
}

void CDlgImageZoom::drawLine( vector<CPoint>& list, CPoint &point )
{
 for (size_t j=1; j<list.size();j++)
 {
   CPoint p1, p2;
    p1=list[j-1];
   p2=list[j];
   p1.Offset(point);
   p2.Offset(point);
   m_memDC.MoveTo(p1);
   m_memDC.LineTo(p2);
 }
}

// CDlgImageZoom message handlers

BOOL CDlgImageZoom::OnEraseBkgnd(CDC* pDC)
{
  return TRUE;
}

void CDlgImageZoom::drawCross()
{
 CPoint p1 = m_center+CPoint(-5,0);
  CPoint p2 = m_center+CPoint(+5,0);
  CPoint p3 = m_center+CPoint(0,-5);
  CPoint p4 = m_center+CPoint(0,+5);
  CPoint c1 = m_center+CPoint(-1,0);
  CPoint c2 = m_center+CPoint(+1,0);
  CPoint c3 = m_center+CPoint(0,-1);
  CPoint c4 = m_center+CPoint(0,+1);
  m_memDC.MoveTo(p1);
 m_memDC.LineTo(c1);
 m_memDC.MoveTo(p2);
 m_memDC.LineTo(c2);
 m_memDC.MoveTo(p3);
 m_memDC.LineTo(c3);
 m_memDC.MoveTo(p4);
 m_memDC.LineTo(c4);
}

void CDlgImageZoom::EraseLines()
{
 m_pointList.clear();
  AddList();
}

4.外层对话框对放大镜的封装及显示
(1).OnInitDialog中初始化

  m_dlgImageZoom.Create(IDD_DLG_IMAGE_ZOOM, this);
  m_dlgImageZoom.MoveWindow(ZOOM_DLG_X, ZOOM_DLG_Y, ZOOM_DLG_WIDTH, ZOOM_DLG_HEIGHT);
 m_dlgImageZoom.SetImagePath(m_FilePath);
  m_dlgImageZoom.SetForegroundWindow();

(2).放大镜子视图函数封装

void CDlgImageOperation::ShowZoom( CPoint point, BOOL isShow/*=TRUE*/, BOOL isLeftDown/*=FALSE*/, BOOL isLine/*=FALSE*/ )
{
 if (isShow)
 {
   m_dlgImageZoom.SetFocus();
    m_dlgImageZoom.UpdateDraw(point, isLeftDown, isLine);
 }
 if (m_dlgImageZoom.IsWindowVisible()!=isShow)
 {
   m_dlgImageZoom.ShowWindow(isShow?SW_SHOW:SW_HIDE);
  }
}

void CDlgImageOperation::AddList()
{
 m_dlgImageZoom.AddList();
}

int CDlgImageOperation::GetHeight()
{
  return m_dlgImageZoom.GetHeight();
}

int CDlgImageOperation::GetWidth()
{
  return m_dlgImageZoom.GetWidth();
}

5.图片处理视图鼠标移动坐标点的计算/转换

void ImageOperationView::OnMouseMove(UINT nFlags, CPoint point)
{
 isMoving = true;

  if (m_currentOperate==IMAGE_OPERATE_DRAW_PEN || m_currentOperate==IMAGE_OPERATE_DRAW_LINE)
  {
   CDlgImageOperation* pDlgImageOperation = (CDlgImageOperation*)GetParent();
    ClientToScreen(&point);
   CPoint tmpPoint = point;
    tmpPoint.Offset(-m_imageRect.TopLeft());
    tmpPoint.x = (LONG)(tmpPoint.x * pDlgImageOperation->GetWidth()/(m_imageRect.Width()*1.0));
   tmpPoint.y = (LONG)(tmpPoint.y * pDlgImageOperation->GetHeight()/(m_imageRect.Height()*1.0));
   CRect tmpRect(m_imageRect.left-30, m_imageRect.top-30, m_imageRect.right+30, m_imageRect.bottom+30);

    if (isLeftDown||tmpRect.PtInRect(point))
    {
     pDlgImageOperation->ShowZoom(tmpPoint, TRUE, isLeftDown, m_currentOperate==IMAGE_OPERATE_DRAW_LINE);
    }
   else
    {
     pDlgImageOperation->ShowZoom(tmpPoint, FALSE, isLeftDown);
    }
   ScreenToClient(&point);
 }
 /*...*/
}

6.需要在图片处理视图的鼠标左键抬起事件中调用AddList函数开始新的曲线绘制。
附件:操作录像
[KGVID width=”640″ height=”386″]http://www.xdty.org/wp-content/uploads/2014/05/Video_2014-05-14_112907_edit.mp4[/KGVID]

发表评论

邮箱地址不会被公开。 必填项已用*标注