如何检索WinForms PictureBox的缩放比例?

11

我需要获取PictureBox上鼠标指针的精确位置。

我使用PictureBox的MouseMove事件。

在这个PictureBox上,我使用“zoom”属性来显示一个图像。

如何正确地获取鼠标在原始(未缩放)图像上的位置?

是否有一种方法可以找到比例因子并使用它?

我认为需要使用imageOriginalSize/imageShowedSize函数来检索比例因子。

我使用这个函数:

float scaleFactorX = mypic.ClientSize.Width / mypic.Image.Size.Width;
float scaleFactorY = mypic.ClientSize.Height / mypic.Image.Size.Height;

这个值是否可以用来获取光标在图像上的正确位置?


我想你可以尝试一下:Point cursorPosition = mypic.PointToClient(Cursor.Position); Point positionOverImage = new Point(cursorPosition.X / scaleFactorX, cursorPosition.Y / scaleFactorY);。然而,如果使用这种方法计算缩放因子,可能会存在一些精度问题。我建议你自己进行图像缩放。 - Lukasz M
谢谢,但是不起作用。位置不正确,当使用pointtoclient和eventmouseargs位置时,我得到相同的值。 - devilkkw
请发布计算您当前使用的坐标的代码片段。 - Lukasz M
Rectangle sourceRec = new Rectangle((int)(e.X / currentScale), (int)(e.Y / currentScale), 1, 1);矩形源Rec = 新矩形((int)(e.X / currentScale), (int)(e.Y / currentScale), 1, 1); - devilkkw
当你说你需要相对于未拉伸的图片获取鼠标坐标时, 你期待未拉伸的图片具有什么锚点?它是以左上角为锚点?还是以中心为锚点? - Satyajit
一个正确的转换需要注意框的SizeMode属性。你现在使用的代码只对Stretch正确,这是一个不寻常的选择。 - Hans Passant
3个回答

16

今天我也遇到了同样的问题。我希望这个解决方案适用于任何宽高比的图像。

以下是我的方法,用于找到原始全尺寸图像上的点'unscaled_p'。

            Point p = pictureBox1.PointToClient(Cursor.Position);
            Point unscaled_p = new Point();

            // image and container dimensions
            int w_i = pictureBox1.Image.Width; 
            int h_i = pictureBox1.Image.Height;
            int w_c = pictureBox1.Width;
            int h_c = pictureBox1.Height;

第一个技巧是确定图像相对于容器是水平还是垂直较大,这样您就可以知道哪个图像尺寸完全填充容器。

            float imageRatio = w_i / (float)h_i; // image W:H ratio
            float containerRatio = w_c / (float)h_c; // container W:H ratio

            if (imageRatio >= containerRatio)
            {
                // horizontal image
                float scaleFactor = w_c / (float)w_i;
                float scaledHeight = h_i * scaleFactor;
                // calculate gap between top of container and top of image
                float filler = Math.Abs(h_c - scaledHeight) / 2;  
                unscaled_p.X = (int)(p.X / scaleFactor);
                unscaled_p.Y = (int)((p.Y - filler) / scaleFactor);
            }
            else
            {
                // vertical image
                float scaleFactor = h_c / (float)h_i;
                float scaledWidth = w_i * scaleFactor;
                float filler = Math.Abs(w_c - scaledWidth) / 2;
                unscaled_p.X = (int)((p.X - filler) / scaleFactor);
                unscaled_p.Y = (int)(p.Y / scaleFactor);
            }

            return unscaled_p;

请注意,由于Zoom会将图像居中显示,所以必须考虑“填充”长度以确定未被图像填充的尺寸。结果,“unscaled_p”是与“p”相关联的未缩放图像上的点。

希望能帮到你!


2
只有一个小错误需要完善:对于比率,您不应该使用完整控件的宽度和高度,而是使用Control.ClientSize的宽度和高度,因此没有边框。很多时候,这种差异并不明显,直到您显示边框并进行一些计算。您会发现缺少一些像素。 - Harald Coppoolse

2
如果我理解正确,我相信你想要做这样的事情:
假设:PictureBox适合图像的宽度/高度,在PictureBox边框和实际图像之间没有空间。
ratioX = e.X / pictureBox.ClientSize.Width;
ratioY = e.Y / pictureBox.ClientSize.Height;

imageX = image.Width * ratioX;
imageY = image.Height * ratioY;

这应该给出原始图像中的点或像素。


我忘记了在比率前加上(float),请添加。目前如果你有一个名为ratioX的变量,那么它很可能是一个int类型,这意味着ratioX/Y将始终为零。 - Dene B

0
这是一个简单的函数来解决这个问题:
private Point RemapCursorPosOnZoomedImage(PictureBox pictureBox, int x, int y, out bool isInImage)
{
  // original size of image in pixel
  float imgSizeX = pictureBox.Image.Width;
  float imgSizeY = pictureBox.Image.Height;

  // current size of picturebox (without border)
  float cSizeX = pictureBox.ClientSize.Width;
  float cSizeY = pictureBox.ClientSize.Height;

  // calculate scale factor for both sides
  float facX = (cSizeX / imgSizeX);
  float facY = (cSizeY / imgSizeY);

  // use smaller one to fit picturebox zoom layout
  float factor = Math.Min(facX, facY);

  // calculate current size of the displayed image
  float rSizeX = imgSizeX * factor;
  float rSizeY = imgSizeY * factor;

  // calculate offsets because image is centered
  float startPosX = (cSizeX - rSizeX) / 2;
  float startPosY = (cSizeY - rSizeY) / 2;

  float endPosX = startPosX + rSizeX;
  float endPosY = startPosY + rSizeY;

  // check if cursor hovers image
  isInImage = true;
  if (x < startPosX || x > endPosX) isInImage = false;
  if (y < startPosY || y > endPosY) isInImage = false;

  // remap cursor position
  float cPosX = ((float)x - startPosX) / factor;
  float cPosY = ((float)y - startPosY) / factor;

  // create new point with mapped coords
  return new Point((int)cPosX, (int)cPosY);
}

你的回答可以通过添加更多关于代码的信息以及它如何帮助提问者来改进。 - Tyler2P
该方法解决了devilkkw所描述的问题。但我同意你的观点。我可能应该更多地谈论一下输入参数。首先,您需要指定要从哪个图片框中提取位置。然后,您指定鼠标的位置 (x,y),该位置将在MouseMove事件中给出。out布尔值在结尾处返回,指示鼠标是否在图片内,在具有“缩放”功能的图片框中可能不是这种情况。该方法的返回值为一个点,该点指定“未缩放”图片的坐标。 - NonoGG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接