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

图像目标缩放(平移缩放)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.32/5 (16投票s)

2008年1月22日

CPOL

2分钟阅读

viewsIcon

106589

downloadIcon

9092

用于平移和缩放到图像中的兴趣点。

InitalLoad.jpg

引言

有几篇关于平移和缩放的文章,但同时进行这两者对您的程序用户来说可能更有用。但是,这确实需要一些时间来适应,因为如果您不习惯,可能会过度平移图像。

背景

这不是一个新想法,CAD程序一直在使用它。 但是,我通常在图像软件中看不到此功能,并且由于它可能很有用,因此我决定编写一个。

使用代码

尝试单击并拖动以平移图像,并使用鼠标滚轮放大和缩小。

Pan.jpg

然后,尝试放大到像眼睛之类的兴趣点,然后看看平移-缩放的工作方式。这需要一些时间来适应,因为您很容易超过目标。

EyeZoom.jpg

Using the Code

由于它不太长或复杂,因此我只发布了以下重要部分。需要注意的一些事情是,我在此演示中使用缓冲图形,并且没有进行广泛的错误检查。我确实对此进行了测试,并试图阻止一些常见的错误。

想象一张非常大的纸上的图像,这就是我们的世界坐标。然后,打开一个窗口,观察世界。您可以向左或向右移动窗口以查看世界的不同部分。您可以向前或向后移动窗口(缩放)以调整您可以看到的世界范围。这是世界坐标和视口坐标背后的基本概念。

public partial class Form1 : Form
{
    public class overRidePanel : Panel
    {
        protected override void OnPaintBackground(PaintEventArgs pevent) { }
    }
    Bitmap bitmap;
    BufferedGraphicsContext currentContext;
    BufferedGraphics myBuffer;  
    PointF viewPortCenter;
    float Zoom = 1.0f;
    bool draging = false;
    Point lastMouse;
    public Form1()
    {
        InitializeComponent();
        currentContext = BufferedGraphicsManager.Current;
        setup(false);
    }
    private void setup(bool resetViewport)
    {            
        if (myBuffer != null)
            myBuffer.Dispose();
        myBuffer = currentContext.Allocate(this.panel1.CreateGraphics(), 
                                           this.panel1.DisplayRectangle);
        if (bitmap != null)
        {
            if (resetViewport)
                SetViewPort(new RectangleF(0, 0, bitmap.Width, bitmap.Height));
        }            
        this.panel1.Focus();
        this.panel1.Invalidate();
    }        
    private void SetViewPort(RectangleF worldCords)
    {           
       //Find smallest screen extent and zoom to that
        if (worldCords.Height > worldCords.Width)
        {
            //Use With as limiting factor
            this.Zoom = worldCords.Width / bitmap.Width;
        }
        else
            this.Zoom = worldCords.Height / bitmap.Height;
        viewPortCenter = new PointF(worldCords.X +(worldCords.Width / 2.0f), 
                                    worldCords.Y + (worldCords.Height / 2.0f));
        this.toolStripStatusLabel1.Text = "Zoom: " + 
                 ((int)(this.Zoom*100)).ToString()+"%";
        
    }
    private void SetViewPort(Rectangle screenCords)
    {
    }
    private void PaintImage()
    {
        if (bitmap != null)
        {
            float widthZoomed = panel1.Width / Zoom;
            float heigthZoomed = panel1.Height / Zoom;
            //Do checks the reason 30,000 is used is because 
            //much over this will cause DrawImage to crash
            if (widthZoomed > 30000.0f)
            {
                Zoom = panel1.Width / 30000.0f;
                widthZoomed = 30000.0f;
            }                
            if (heigthZoomed > 30000.0f)
            {
                Zoom = panel1.Height / 30000.0f;
                heigthZoomed = 30000.0f;
            }
            //we stop at 2 because at this point you have almost zoomed into a single pixel
            if (widthZoomed < 2.0f)
            {
                Zoom = panel1.Width / 2.0f;
                widthZoomed = 2.0f;
            }
            if (heigthZoomed < 2.0f)
            {
                Zoom = panel1.Height / 2.0f;
                heigthZoomed = 2.0f;
            }
            float wz2 = widthZoomed / 2.0f;
            float hz2 = heigthZoomed / 2.0f;
            Rectangle drawRect = new Rectangle(
                (int)(viewPortCenter.X - wz2),
                (int)(viewPortCenter.Y - hz2),
                (int)(widthZoomed), 
                (int)(heigthZoomed));
            //this.toolStripStatusLabel1.Text = "DrawRect = " + drawRect.ToString();
            
            myBuffer.Graphics.Clear(Color.White); //Clear the Back buffer
            //Draw the image, Write image to back buffer, and render back buffer
            myBuffer.Graphics.DrawImage(bitmap, 
                     this.panel1.DisplayRectangle, drawRect, GraphicsUnit.Pixel);
            myBuffer.Render(this.panel1.CreateGraphics());
           this.toolStripStatusLabel1.Text = "Zoom: " + 
                    ((int)(this.Zoom * 100)).ToString() + "%";
        }
    }        
    private void openToolStripMenuItem_Click(object sender, EventArgs e)
    {
        if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            bitmap = (Bitmap)Bitmap.FromFile(openFileDialog1.FileName);
            setup(true);
        }
    }
    private void Form1_Resize(object sender, EventArgs e)
    {
        setup(false);
    }
    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        PaintImage();
    }
    private void panel1_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
    {   
        //the 1200.0f is any-number it just seem to work well so i use it.
        Zoom += Zoom * (e.Delta / 1200.0f);
        if (e.Delta > 0)
        //I prefer to use the targe zoom when zooming in only, 
        //remove "if" to have it apply to zoom in and out
            viewPortCenter = new PointF(viewPortCenter.X + 
               ((e.X - (panel1.Width / 2)) /(2* Zoom)), viewPortCenter.Y + 
               ((e.Y - (panel1.Height/2)) / (2*Zoom)));
        this.panel1.Invalidate();
    }
    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
            draging = true;
    }
    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        if (draging)
        {
            viewPortCenter = new PointF(viewPortCenter.X + 
              ((lastMouse.X - e.X)/Zoom), viewPortCenter.Y + ((lastMouse.Y- e.Y)/Zoom));
            panel1.Invalidate();
        }
        lastMouse = e.Location;
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
            draging = false;
    }
}

关注点

有几件事。首先,您会注意到,放大有时会变得块状或笨拙;这是因为平移移动了大量的距离。我通过除以2来减少了这种情况,但是很明显。要解决此问题并使其更流畅,您可以将最大平移量设置为常量或缩放级别的某个比例。上面发布的方法完成了目标,因此我没有追求更精细的细节。请注意,我创建了一个类,其存在的全部目的和目的是覆盖标准Panel对象的OnPaintBackground。这样做是为了消除普通Panel进行背景绘制所产生的闪烁。

历史

这是第二个这样的程序。由于此版本执行得更好,因此我发布了它。

© . All rights reserved.