c# - 在 PictureBox 处于“缩放”模式时裁剪图像的正确部分
问题描述
我有一个 PictureBox1,它的 sizemode 设置为 Stretch 和 PictureBox1。PictureBox1 包含一个图像,让我选择它的一部分,然后裁剪它并将裁剪的部分存储在 PictureBox2 中。当 sizemode 设置为 Stretch 并且图片未缩放时效果很好,但当我缩放它或将 sizemode 设置为缩放时则不行。
我用来裁剪部分图片的代码(原始来源)
try
{
float stretch1X = 1f * pictureBox1.Image.Width / pictureBox1.ClientSize.Width;
float stretch1Y = 1f * pictureBox1.Image.Height / pictureBox1.ClientSize.Height;
Point pt = new Point((int)(_mDown.X * stretch1X), (int)(_mDown.Y * stretch1Y));
Size sz = new Size((int)((_mCurr.X - _mDown.X) * stretch1X),
(int)((_mCurr.Y - _mDown.Y) * stretch1Y));
if (sz.Width > 0 && sz.Height > 0)
{
Rectangle rSrc = new Rectangle(pt, sz);
Rectangle rDest = new Rectangle(Point.Empty, sz);
Bitmap bmp = new Bitmap(sz.Width, sz.Height);
using (Graphics G = Graphics.FromImage(bmp))
G.DrawImage(pictureBox1.Image, rDest, rSrc, GraphicsUnit.Pixel);
return bmp;
}
return null;
}
catch (Exception ex)
{
throw ex;
}
如何正确计算?如何使裁剪功能以某种方式工作,以便用户放大/缩小并仍然裁剪图片的正确部分?
解决方案
您需要使用拉伸因子以及偏移量来计算点。
因为Zoom
只有一个因素,因为 和 的纵横比始终相同Image
,PictureBox
但通常有一个偏移量;因为Stretch
你不需要抵消,但需要两个因素。
这是一个使用两个 PictureBoxes
两个显示缩放版本和裁剪位图的示例。它利用了一个ImageArea
确定大小和偏移量的通用函数。
两个类级别变量:
Point pDown = Point.Empty;
Rectangle rect = Rectangle.Empty;
三个鼠标事件:
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
pDown = e.Location;
pictureBox1.Refresh();
}
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (!e.Button.HasFlag(MouseButtons.Left)) return;
rect = new Rectangle(pDown, new Size(e.X - pDown.X, e.Y - pDown.Y));
using (Graphics g = pictureBox1.CreateGraphics())
{
pictureBox1.Refresh();
g.DrawRectangle(Pens.Orange, rect);
}
}
private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
{
Rectangle iR = ImageArea(pictureBox2);
rect = new Rectangle(pDown.X - iR.X, pDown.Y - iR.Y,
e.X - pDown.X, e.Y - pDown.Y);
Rectangle rectSrc = Scaled(rect, pictureBox2, true);
Rectangle rectDest = new Rectangle(Point.Empty, rectSrc.Size);
Bitmap bmp = new Bitmap(rectDest.Width, rectDest.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(pictureBox2.Image, rectDest, rectSrc, GraphicsUnit.Pixel);
}
pictureBox2.Image = bmp;
}
这是一个有用的函数,它返回任何尺寸模式的图片框内实际图像的区域..:
Rectangle ImageArea(PictureBox pbox)
{
Size si = pbox.Image.Size;
Size sp = pbox.ClientSize;
if (pbox.SizeMode == PictureBoxSizeMode.StretchImage)
return pbox.ClientRectangle;
if (pbox.SizeMode == PictureBoxSizeMode.Normal ||
pbox.SizeMode == PictureBoxSizeMode.AutoSize)
return new Rectangle(Point.Empty, si);
if (pbox.SizeMode == PictureBoxSizeMode.CenterImage)
return new Rectangle(new Point((sp.Width - si.Width) / 2,
(sp.Height - si.Height) / 2), si);
// PictureBoxSizeMode.Zoom
float ri = 1f * si.Width / si.Height;
float rp = 1f * sp.Width / sp.Height;
if (rp > ri)
{
int width = si.Width * sp.Height / si.Height;
int left = (sp.Width - width) / 2;
return new Rectangle(left, 0, width, sp.Height);
}
else
{
int height = si.Height * sp.Width / si.Width;
int top = (sp.Height - height) / 2;
return new Rectangle(0, top, sp.Width, height);
}
}
我们只需要偏移量来确定未缩放的矩形。我们还需要对其进行缩放:
Rectangle Scaled(Rectangle rect, PictureBox pbox, bool scale)
{
float factor = GetFactor(pbox);
if (!scale) factor = 1f / factor;
return Rectangle.Round(new RectangleF(rect.X * factor, rect.Y * factor,
rect.Width * factor, rect.Height * factor));
}
为此需要知道比例因子,它取决于纵横比:
float GetFactor(PictureBox pBox)
{
if (pBox.Image == null) return 0;
Size si = pBox.Image.Size;
Size sp = pBox.ClientSize;
float ri = 1f * si.Width / si.Height;
float rp = 1f * sp.Width / sp.Height;
float factor = 1f * pBox.Image.Width / pBox.ClientSize.Width;
if (rp > ri) factor = 1f * pBox.Image.Height / pBox.ClientSize.Height;
return factor;
}
如果通过将其放在 aPictureBox
内并AutoScrolling Panel
更改Pbox.Size
.