Windows XP 平板电脑版GDI+Windows VistaWindows 2003Visual Studio 2005Windows 2000设计 / 图形Windows XP.NET 2.0C# 2.0初学者开发Visual StudioWindows.NETC#
图像目标缩放(平移缩放)






4.32/5 (16投票s)
用于平移和缩放到图像中的兴趣点。
引言
有几篇关于平移和缩放的文章,但同时进行这两者对您的程序用户来说可能更有用。但是,这确实需要一些时间来适应,因为如果您不习惯,可能会过度平移图像。
背景
这不是一个新想法,CAD程序一直在使用它。 但是,我通常在图像软件中看不到此功能,并且由于它可能很有用,因此我决定编写一个。
使用代码
尝试单击并拖动以平移图像,并使用鼠标滚轮放大和缩小。
然后,尝试放大到像眼睛之类的兴趣点,然后看看平移-缩放的工作方式。这需要一些时间来适应,因为您很容易超过目标。
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
进行背景绘制所产生的闪烁。
历史
这是第二个这样的程序。由于此版本执行得更好,因此我发布了它。