У меня есть PictureBox1 с его sizemode, установленным на Stretch и PictureBox1. PictureBox1 содержит изображение, и позвольте мне выбрать его часть, затем обрезать и сохранить обрезанную часть внутри PictureBox2. Он отлично работает когда sizemode установлен на Stretch и изображение не увеличено, но не тогда, когда я увеличиваю его или устанавливаю sizemode для увеличения.
рабочий пример - для sizemode установлено значение «stretch»
Код, который я использую для обрезки части изображения (первоисточник)
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;
}
Как правильно рассчитать? Как заставить функцию обрезки работать таким образом, чтобы пользователь мог увеличивать/уменьшать масштаб и при этом обрезать правильную часть изображения?
Как я могу применить это к своему сценарию? Не могли бы вы объяснить немного больше? Ваше объяснение мне не совсем понятно @HansPassant
Вам нужно знать размер отображаемого изображения, этот метод вычисляет его. Просто продублируйте код для случая масштабирования. Как только вы узнаете этот размер, вы можете разделить его на image.Width, чтобы узнать stretch1X. И, таким образом, stretch1Y. Отображаемое изображение будет помещено в рамки по горизонтали или по вертикали со смещением на разницу между размером отображаемого изображения и PictureBox.ClientSize, деленную на 2.
Это немного сбивает с толку, можете ли вы опубликовать код подсказки, основанный на коде, который я разместил выше? Заранее спасибо @HansPassant
@Hans: Ты совершенно прав! Забавно, как я не смог найти этот пост..!? Я оставляю ответ, потому что он также создает обрезанное изображение и демонстрирует, что он также работает в режиме увеличения.
Вам нужно рассчитать точки, используя коэффициент растяжения и, возможно, также смещение.
Для 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;
}
Это решение также будет работать, если PictureBox
равно увеличено или уменьшено, поместив его внутрь AutoScrolling Panel
и изменив Pbox.Size
.
Когда он увеличен, тогда stretch1X == stretch1Y. Смотри сюда.