【fishing-pan:https://blog.csdn.net/u013921430 转载请注明出处】

  最近接触了一下C# 的内容,遇到一个需求,就是关于图像的的缩放。查找了网上的一些资源,发现这些方法存在一些或多或少的问题,而且大部分都是相互抄袭的。所以今天花点时间把自己查阅资料和实践的心得分享出来,希望对有需要的人有所帮助。
  如果这篇博客对你有所帮助,请点个赞哈。

准备工作

  在创建了C#的工程后,为了实现图像缩放,首先需要添加一个 Panel 容器至窗口中;然后再向 Panel 中添加一个 PictureBox 公共控件,并且将 PictureBoxSizeMode 设置为Zoom,最后将 Panel 容器停靠在父窗口。至此,准备工作就结束了。添加进父窗口的方法就是点击下图中的小三角形,然后选择停靠在父窗口。

在这里插入图片描述

创建事件

  在做完准备工作后就要添加事件了,但是在自带的事件中并没有MouseWheel事件,所以需要自己添加。

  首先在Designer.cs文件的panel部分添加以下事件声明,以创建鼠标滚动事件。

this.panel1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel);

实现缩放事件

  在实现缩放事件前,首先需要明确一些事情。

  1. 这里采用的方法是通过改变窗口大小来进行缩放的;
  2. 网络上的很多资料通过改变 PictureBox 相对于 panel 的位置来实现居中或者根据鼠标位置定点缩放。就是后面的的方式; this.pictureBox.pictureBox.Location = point(Left, Top);
  3. 窗口滑条有两个很重要的特征,首先就是滑条的长度表示窗口的大小,也就是 panel 的高和宽;第二,滑条的位置对于水平滑条来说,是滑条左侧端点的位置,垂直滑条的位置是上端点的位置。(这一条听起来有点懵,后面会解释)

  那么首先来说第二点;网络上常用的这种方法确实可以实现定点缩放;但是由于改变位置后的左上点一般是负值;会导致在窗口左侧和上侧的图像无法访问,试过的人应该都知道。是不可取的,但是我们通过修改滑条的位置来实现定点缩放就不会存在这种问题。所以我们在上面会提到滑条的一些特性。
  再来说说第三点;我们知道,当图像大小小于或者等于窗口大小时,是不需要滑条的,因为此时窗口可以放下图像;当图像大于窗口大小时,则需要滑条来调节。而滑条所在的整个区间的代表的是 PictureBox 的尺寸,滑条代表窗口的大小。几者之间满足以下等式;
L e n g t h S c r o l l L e n g t h R a n g e = S i z e W i n d o w S i z e P i c t u r e B o x \frac{LengthScroll}{LengthRange}=\frac{SizeWindow}{SizePictureBox} LengthRangeLengthScroll=SizePictureBoxSizeWindow

  从上面的关系式不难分析,滑条端点的位置取决于窗口展示的图像在整个 PictureBox 中的相对位置。了解以上关系,就可以实现定点缩放,接下来以窗口中心定点缩放来进行分析;

   PictureBox 相对于 panel 的位置 LeftTop 是可以直接获得的属性; panel 的尺寸 WidthHeight 也是可以直接获得的属性。根据这两个属性我们可以知道窗口中心位置的像素 CentorPictureBox 中的位置为;

int iOriginCentorX=(this.panel1.Width/2-this.pictureBox.Left);
int iOriginCentorY=(this.panel1.Height/2-this.pictureBox.Top);

  而在了解缩放比例后,中心点在图像中的坐标也是可以知道的;而窗口大小是不变的;就可以求出位于窗口左上位置的像素点在图像中的坐标;
n e w L e f t = n e w C e n t o r X − W i n d o w W i d t h 2 newLeft=newCentorX-\frac{WindowWidth}{2} newLeft=newCentorX2WindowWidth
n e w T o p = n e w C e n t o r Y − W i n d o w H e i g h t 2 + D e l t a newTop=newCentorY-\frac{WindowHeight}{2}+Delta newTop=newCentorY2WindowHeight+Delta
  而这个位置就是我们需要的滑条的位置。但是有个问题,滑动滚轮时,滑条位置会被改变;所以上面的公式中垂直滑条的位置要加上一个Delta,既是滚轮滑动一次,滑条滑动的距离。下面就是函数的代码。完整的项目资源,我放在了这里

private void panel1_MouseWheel(object sender, MouseEventArgs e)
        {
            if (Control.ModifierKeys==Keys.Control)
            {
                float fZoomFactor = 1.2f;      // 缩放因子
                int iOriginCentorX=(this.panel1.Width/2-this.pictureBox.Left);      //缩放前
                int iOriginCentorY=(this.panel1.Height/2-this.pictureBox.Top);

                //防止无限制的缩放
                if ((this.pictureBox.Width < 200 && e.Delta < 0) || (this.pictureBox.Width > 10000 && e.Delta > 0))
                    return;

                if(e.Delta>0)
                {
                    //缩放后的picture box的大小。
                    this.pictureBox.Width = (int)(fZoomFactor * this.pictureBox.Width);
                    this.pictureBox.Height = (int)(fZoomFactor * this.pictureBox.Height);
                    //缩放后的中心点距离picture box左上角的距离。
                    int iNewCentorX=(int)(iOriginCentorX*fZoomFactor);
                    int iNewCentorY=(int)(iOriginCentorY*fZoomFactor);

                    //缩放后的滚轮的位置。
                    this.panel1.AutoScrollPosition = new Point((int)(iNewCentorX - this.panel1.Width / 2),
                        (int)(iNewCentorY - this.panel1.Height / 2 + e.Delta));
                }
                else
                {   
                    this.pictureBox.Width = (int)(this.pictureBox.Width / fZoomFactor);
                    this.pictureBox.Height = (int)(this.pictureBox.Height / fZoomFactor);

                    int iNewCentorX = (int)(iOriginCentorX / fZoomFactor);
                    int iNewCentorY = (int)(iOriginCentorY / fZoomFactor);

                    this.panel1.AutoScrollPosition = new Point((int)(iNewCentorX - this.panel1.Width / 2),
                        (int)(iNewCentorY - this.panel1.Height / 2 + e.Delta));
                }
            }
            
        }

另一种缩放图像的方法。

  上面的过程,只是修改了装载图像的Picture Box的大小,而没有真正的修改图像的大小;所以当你保存图像的话,应该还是原图的大小。这里我提供一种修改了图像大小的缩放方法(就是Bitmap的缩放方法)。

private void panel1_MouseWheel(object sender, MouseEventArgs e)
        {
            if (Control.ModifierKeys == Keys.Control)
            {
                float fZoomFactor = 1.2f;      // 缩放因子
                int iOriginCentorX = (this.panel1.Width / 2 - this.pictureBox.Left);      //缩放前
                int iOriginCentorY = (this.panel1.Height / 2 - this.pictureBox.Top);

                //防止无限制的缩放
                if ((this.pictureBox.Width < 200 && e.Delta < 0) || (this.pictureBox.Width > 10000 && e.Delta > 0))
                    return;

                if (e.Delta > 0)
                {
                    //缩放后的picture box的大小。
                    int iNewWidth = (int)(fZoomFactor * this.pictureBox.Width);
                    int iNewHeight = (int)(fZoomFactor * this.pictureBox.Height);

                    //定义新的Bitmap;
                    Bitmap BitNewImg = new Bitmap(iNewWidth, iNewHeight);
                    //定义对BitNewImg的绘制方法;
                    Graphics graph = Graphics.FromImage(BitNewImg);
                    graph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
                    //用原始图像绘制新图像;
                    graph.DrawImage((Bitmap)this.pictureBox.Image, new Rectangle(0, 0, iNewWidth, iNewHeight),
                        new Rectangle(0, 0, this.pictureBox.Width, this.pictureBox.Height), GraphicsUnit.Pixel);
                    //缩放后的中心点距离picture box左上角的距离。
                    int iNewCentorX = (int)(iOriginCentorX * fZoomFactor);
                    int iNewCentorY = (int)(iOriginCentorY * fZoomFactor);

                    this.pictureBox.Image = BitNewImg;
                    this.pictureBox.Width = this.pictureBox.Image.Width;
                    this.pictureBox.Height = this.pictureBox.Image.Height;
                    //缩放后的滚轮的位置。
                    this.panel1.AutoScrollPosition = new Point((int)(iNewCentorX - this.panel1.Width / 2),
                        (int)(iNewCentorY - this.panel1.Height / 2 + e.Delta));

                    graph.Dispose();
                }
                else
                {
                    int iNewWidth = (int)(this.pictureBox.Width / fZoomFactor);
                    int iNewHeight = (int)(this.pictureBox.Height / fZoomFactor);

                    Bitmap BitNewImg = new Bitmap(iNewWidth, iNewHeight);
                    //定义对BitNewImg的绘制方法;
                    Graphics graph = Graphics.FromImage(BitNewImg);
                    graph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
                    //用原始图像绘制新图像;
                    graph.DrawImage((Bitmap)this.pictureBox.Image, new Rectangle(0, 0, iNewWidth, iNewHeight),
                        new Rectangle(0, 0, this.pictureBox.Width, this.pictureBox.Height), GraphicsUnit.Pixel);

                    this.pictureBox.Image = BitNewImg;
                    this.pictureBox.Width = this.pictureBox.Image.Width;
                    this.pictureBox.Height = this.pictureBox.Image.Height;

                    int iNewCentorX = (int)(iOriginCentorX / fZoomFactor);
                    int iNewCentorY = (int)(iOriginCentorY / fZoomFactor);

                    this.panel1.AutoScrollPosition = new Point((int)(iNewCentorX - this.panel1.Width / 2),
                        (int)(iNewCentorY - this.panel1.Height / 2 + e.Delta));
                }
            }

        }

最后

  从文章可以看出,其实图像缩放的实现并不难,重要的是理解滑条的位置的意义,从而决定缩放后滑条的位置,以达到定点缩放。虽然这篇博客讲的是以窗口中心的像素点进行定点缩放,但是了解了原理,根据鼠标位置进行缩放也并不难。
  如果这篇博客对你有所帮助,就点个赞吧。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐