【问题标题】:How zoom into a drawn rectangle of picturebox which is inside a panel?如何放大面板内的图片框的绘制矩形?
【发布时间】:2016-12-29 21:28:54
【问题描述】:

我在面板中有一个 PictureBox 用于缩放和平移。我创造了通过鼠标单击选择 4 个点并在 PictureBox 上绘制一个矩形的可能性。一旦矩形在我的图片上,我将矩形的坐标传递给方法“cropRectangle”。此方法裁剪矩形并用裁剪后的图像替换旧图像。 效果很好

(OriginalImage是pictureBox中实际图像的位图)

private void cropRectangle(Rectangle rect){
    double left = (rect.X) * originalImage.Width / pictureBox.Width,
    top = (rect.Y) * originalImage.Width / pictureBox.Height,
    right = (rect.Width) * originalImage.Width / pictureBox.Width,
    bottom = (rect.Height) * originalImage.Height / pictureBox.Height;
    rect = new Rectangle (Convert.ToInt32(left), Convert.ToInt32(top), Convert.ToInt32(right), Convert.ToInt32(bottom));

    Bitmap bitmap = orignalImage.Clone(rect, originalImage.PixelFormat);
    pictureBox.Image = (Image)bitmap; 

    centerPictureBox();

    // fit image into pictureBox with respect to the ratio
    float ratio = orignalImage.Width / orignalImage.Height;
    pictureBox.Width = panel.Width;
    pictureBox.Height = Convert.ToInt32(pictureBox.Width * ratio);
    centerPictureBox();
}

我现在要做的是缩放选定区域而不是裁剪它。图片框的矩形必须与面板匹配。

如何通过面板只显示图片框的选定区域(矩形)而不裁剪图像?

【问题讨论】:

    标签: c# winforms zooming panel picturebox


    【解决方案1】:

    您应该坚持使用Graphics 对象修改现有的Bitmap,而不是更改图片框的大小。当所需功能在其他地方已经可用时,您不希望被绑定到 UI 控件。

    以下是实现这一目标的粗略步骤:

    1. 创建一个临时的Bitmap 对象来存储缩放的图像。 Bitmap tBitmap = new Bitmap(zoomX, zoomY, PixelFormat.Format24bppRgb);
    2. 当您想要缩放时,计算缩放系数和您已经做的事情(我没有检查代码是否正确,但我认为它是正确的)。
    3. 从临时位图创建一个新的图形对象。 Graphics graphics = Graphics.FromImage(tBitmap);
    4. 设置InterpolationMode 以使图像以良好的质量缩放。 graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
    5. 使用DrawImage 方法(使用PictureBox 中的原始图像)绘制缩放图像。 graphics.DrawImage(pictureBox.Image, new Rectangle(0, 0, pictureBox.Width, pictureBox.Height), new Rectangle(/*The crop rectangle you draw already*/), GraphicsUnit.Pixel);
    6. 将新绘制的位图设置为 PictureBox 中的图像。 pictureBox.Image = tBitmap;
    7. 记得处置我们用于绘图的图形对象。 graphics.Dispose();
    8. 您可能需要刷新 PictureBox 以强制它重新绘制自身。 pictureBox.Refresh();

    这些是要遵循的基本步骤。我没有时间深入研究您现有的代码,因此您可能需要更改一些其他内容才能使其正常工作。

    这里还有一篇涵盖相同内容的 MSDN 文章:Cropping and Scaling Images in GDI+

    【讨论】:

    • @dll 我记得在 Codeproject.com 上看到过很多此类功能的工作示例。您可以搜索“图片框缩放窗口窗体”之类的内容。
    • 谢谢,但我刚刚找到了解决问题的方法。
    【解决方案2】:

    您可能对这个允许缩放和平移图片框的控件 (ZoomPicBox) 感兴趣。

    此代码的所有功劳均归于 Bob Powell,它取自他的网站(现在似乎已关闭,并且已经关闭了很长时间。

    我在这个链接从archive.org复制了代码:

    https://web.archive.org/web/20080313161349/http://www.bobpowell.net/zoompicbox.htm

    该链接包含更多信息,值得一读。该代码在 VB.Net 中也可用。

    我不知道 Bob Powell 的站点为何关闭,但它是一个很好的 Windows 图形信息站点。

    我觉得这段代码值得重复。这个控件可以拖到窗体上。

    namespace bobpowell.net
    {
      /// <summary>
      /// ZoomPicBox does what it says on the wrapper.
      /// </summary>
      /// <remarks>
      /// PictureBox doesn't lend itself well to overriding. Why not start with something basic and do the job properly?
      /// </remarks>
      public class ZoomPicBox : ScrollableControl
      {
    
        Image _image;
        [
        Category("Appearance"),
        Description("The image to be displayed")
        ]
        public Image Image
        {
          get{return _image;}
          set
          {
            _image=value;
            UpdateScaleFactor();
            Invalidate();
          }
        }
    
        float _zoom=1.0f;
        [
        Category("Appearance"),
        Description("The zoom factor. Less than 1 to reduce. More than 1 to magnify.")
        ]
        public float Zoom
        {
          get{return _zoom;}
          set
          {
            if(value<0 || value<0.00001)
              value=0.00001f;
            _zoom=value;
            UpdateScaleFactor();
            Invalidate();
          }
        }
    
        /// <summary>
        /// Calculates the effective size of the image
        ///after zooming and updates the AutoScrollSize accordingly
        /// </summary>
        private void UpdateScaleFactor()
        {
          if(_image==null)
            this.AutoScrollMinSize=this.Size;
          else
          {
            this.AutoScrollMinSize=new Size(
              (int)(this._image.Width*_zoom+0.5f),
              (int)(this._image.Height*_zoom+0.5f)
              );
          }
        }
    
        InterpolationMode _interpolationMode=InterpolationMode.High;
        [
        Category("Appearance"),
        Description("The interpolation mode used to smooth the drawing")
        ]
        public InterpolationMode InterpolationMode
        {
          get{return _interpolationMode;}
          set{_interpolationMode=value;}
        }
    
    
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
          // do nothing.
        }
    
        protected override void OnPaint(PaintEventArgs e)
        {
          //if no image, don't bother
          if(_image==null)
          {
            base.OnPaintBackground(e);
            return;
          }
          //Set up a zoom matrix
          Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);
          //now translate the matrix into position for the scrollbars
          mx.Translate(this.AutoScrollPosition.X / _zoom, this.AutoScrollPosition.Y / _zoom);
          //use the transform
          e.Graphics.Transform=mx;
          //and the desired interpolation mode
          e.Graphics.InterpolationMode=_interpolationMode;
          //Draw the image ignoring the images resolution settings.
          e.Graphics.DrawImage(_image,new Rectangle(0,0,this._image.Width,this._image.Height),0,0,_image.Width, _image.Height,GraphicsUnit.Pixel);
          base.OnPaint (e);
        }
    
    
        public ZoomPicBox()
        {
          //Double buffer the control
          this.SetStyle(ControlStyles.AllPaintingInWmPaint |
            ControlStyles.UserPaint |
            ControlStyles.ResizeRedraw |
            ControlStyles.UserPaint |
            ControlStyles.DoubleBuffer, true);
    
          this.AutoScroll=true;
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      我找到了一个优雅的解决方案:

      private void zoomInsideRectangle(Rectangle rect){
          float zoomFactor = ((float)panel.Width / rect.Width) - 1;
      
          pictureBox.Width = pictureBox.Width + convertToIntPerfect(pictureBox.Width * zoomFactor);
          pictureBox.Height = pictureBox.Height + convertToIntPerfect(pictureBox.Height * zoomFactor);
      
          rect.X = rect.X + convertToIntPerfect(rect.X * zoomFactor);
          rect.Y = rect.Y + convertToIntPerfect(rect.Y * zoomFactor);
      
          pictureBox.Left = convertToIntPerfect(-rect.X);
          pictureBox.Top = convertToIntPerfect(-rect.Y);
      }
      

      因为我知道可以看到图片框的面板的长度。我采用面板的比例和要放大的矩形的宽度。这个比例是我的缩放比例。

      我将图片框的大小乘以我计算的比率。

      我用矩形的坐标将图片框固定在左侧和顶部。但在此之前,我必须将矩形的坐标乘以缩放比例,因为我更改了图片框的大小。

      我没有实现 Y 变换,因为图像的原始比例会被破坏。

      【讨论】:

        猜你喜欢
        • 2018-10-23
        • 2016-02-07
        • 1970-01-01
        • 2015-07-11
        • 2012-06-16
        • 1970-01-01
        • 2013-09-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多