Когда вы используете Control в качестве контейнера для изображения, а изображение масштабируется так, чтобы соответствовать границам контейнера (например, устанавливая PictureBox.SizeMode в PictureBoxSizeMode.Zoom ), такИзображение может отображаться в пользовательском интерфейсе с предопределенными показателями, когда вам нужно выбрать раздел изображения, вам нужно рассчитать масштабный коэффициент.Другими словами, определите соотношение между размером контейнера и реальным размером изображения.
Может быть лучше использовать меньший контейнер в качестве эталона, поэтому вместо умножения относительных мер на коэффициент масштабирования можно затем умножить:
private float GetImageScaledRatio(RectangleF canvas, SizeF imageSize)
{
return Math.Max(canvas.Width, canvas.Height) /
Math.Max(imageSize.Width, imageSize.Height);
}
Положение объектива внутриКонтейнер - если вы хотите, чтобы объектив следовал за положением указателя мыши - задается координатами Указателя минус половина размера объектива:
private PointF GetLensPosition(PointF centerPosition, RectangleF lens)
{
return new PointF(centerPosition.X - (lens.Width / 2),
centerPosition.Y - (lens.Height / 2));
}
Чтобы определить фактический размер измерения объектива (выбора) вПо отношению к фактическому размеру растрового изображения размер линзы должен быть масштабирован, когда необходимо отрисовать или обрезать участок растрового изображения:
private SizeF GetScaledLensSize(RectangleF canvas, SizeF imageSize, SizeF lensSize)
{
float scaleRatio = GetImageScaledRatio(canvas, imageSize);
return new SizeF(lensSize.Width * scaleRatio, lensSize.Width * scaleRatio);
}
Кроме того, при отображении предварительного просмотра текущего выделения, представленногоОбъектив, выбор должен быть масштабирован до размера Контейнера, используемого для предварительного просмотра выбора Объектива:
private RectangleF CanvasToImageRect(RectangleF canvas, SizeF imageSize, RectangleF rect)
{
float scaleRatio = GetImageScaledRatio(canvas, imageSize);
return new RectangleF(new PointF(rect.X / scaleRatio, rect.Y / scaleRatio),
new SizeF(rect.Width / scaleRatio, rect.Height / scaleRatio));
}
Эти простые методы позволяют рассчитать как фактический размер выделения по отношению к изображению.учитывается и размер элементов управления, используемых для предварительного просмотра.
При рисовании предварительного просмотра с использованием выделения «Объектив» может быть хорошей идеей использовать общий метод для рисования раздела «Изображение»: метод, который также можно использовать для рисования выделения в новом растровом изображении, которое можетзатем сохраняются на диск или иным образом сохраняются.
Здесь pctLens
- это PictureBox, используемый для предварительного просмотра, RectangleF section
- это мера объектива, масштабированная до размера pctLens
(для предварительного просмотра)и, конечно, sourceImage
- это исходное изображение:
private void pctLens_Paint(object sender, PaintEventArgs e)
{
RectangleF section = CanvasToImageRect(pctOriginal.ClientRectangle, sourceImage.Size, imageLens);
DrawImageSelection(e.Graphics, pctLens.ClientRectangle, section, sourceImage);
}
private void DrawImageSelection(Graphics g, RectangleF canvas, RectangleF imageSection, Image image)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, canvas, imageSection, GraphicsUnit.Pixel);
switch (lensType)
{
case LensType.Circular:
using (var path = new GraphicsPath())
{
path.AddEllipse(canvas);
g.SetClip(path, CombineMode.Exclude);
using (var brush = new SolidBrush(Color.FromArgb(160, Color.Black)))
{
g.FillRectangle(brush, canvas);
g.ResetClip();
using (var pen = new Pen(brush, 1f))
g.DrawEllipse(pen, canvas);
}
}
break;
case LensType.Rectangular:
// NOP
break;
}
}
Визуальный результат (Изображение: 1200x675
, PictureBox: 300x175
, SizeMode: Zoom
)
![Image Lens preview](https://i.stack.imgur.com/8soLR.gif)
Полный исходный код для воспроизведения того, что показано в анимации:
Bitmap sourceImage
- это исходное растровое изображение, его необходимо установить для существующего объекта.
RectangleF imageLens
- это форма, используемая для определения относительного размера объектива.
Size lensPixelSize
- это размер imageLens
в пикселях относительно пользовательского интерфейса.
pctOriginal
- это PictureBox, где отображается исходное изображение.
pctLens
- это PictureBox, где сечение объектива Предварительный просмотр нарисован.
Bitmap sourceImage = null;
RectangleF imageLens = RectangleF.Empty;
Size lensPixelSize = new Size(100, 100);
LensType lensType = LensType.Circular;
bool lensUseRelativeSize = false;
bool drawLens = false;
private enum LensType
{
Circular,
Rectangular
}
private void pctOriginal_MouseMove(object sender, MouseEventArgs e)
{
imageLens.Location = GetLensPosition(e.Location, imageLens);
imageLens.Size = lensUseRelativeSize
? GetScaledLensSize(pctOriginal.ClientRectangle, sourceImage.Size, lensPixelSize)
: lensPixelSize;
pctOriginal.Invalidate();
pctLens.Invalidate();
}
private PointF GetLensPosition(PointF centerPosition, RectangleF rect)
{
return new PointF(centerPosition.X - (rect.Width / 2),
centerPosition.Y - (rect.Height / 2));
}
private SizeF GetScaledLensSize(RectangleF canvas, SizeF imageSize, SizeF lensSize)
{
float scaleRatio = GetImageScaledRatio(canvas, imageSize);
return new SizeF(lensSize.Width * scaleRatio, lensSize.Width * scaleRatio);
}
private float GetImageScaledRatio(RectangleF canvas, SizeF imageSize)
{
return Math.Max(canvas.Width, canvas.Height) /
Math.Max(imageSize.Width, imageSize.Height);
}
private RectangleF CanvasToImageRect(RectangleF canvas, SizeF imageSize, RectangleF rect)
{
float scaleRatio = GetImageScaledRatio(canvas, imageSize);
return new RectangleF(new PointF(rect.X / scaleRatio, rect.Y / scaleRatio),
new SizeF(rect.Width / scaleRatio, rect.Height / scaleRatio));
}
private void pctOriginal_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Red, 2.0f))
{
pen.DashStyle = DashStyle.Dash;
switch (lensType)
{
case LensType.Circular:
e.Graphics.DrawEllipse(pen, Rectangle.Round(imageLens));
break;
case LensType.Rectangular:
e.Graphics.DrawRectangle(pen, Rectangle.Round(imageLens));
break;
}
}
}
private void pctLens_Paint(object sender, PaintEventArgs e)
{
if (!drawLens) return;
RectangleF section = CanvasToImageRect(pctOriginal.ClientRectangle, sourceImage.Size, imageLens);
DrawImageSelection(e.Graphics, pctLens.ClientRectangle, section, sourceImage);
}
private void DrawImageSelection(Graphics g, RectangleF canvas, RectangleF imageSection, Image image)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, canvas, imageSection, GraphicsUnit.Pixel);
switch (lensType)
{
case LensType.Circular:
using (var path = new GraphicsPath())
{
path.AddEllipse(canvas);
g.SetClip(path, CombineMode.Exclude);
using (var brush = new SolidBrush(Color.FromArgb(160, Color.Black)))
{
g.FillRectangle(brush, canvas);
g.ResetClip();
using (var pen = new Pen(brush, 1f))
g.DrawEllipse(pen, canvas);
}
}
break;
case LensType.Rectangular:
// NOP
break;
}
}
private void chkSizeRelative_CheckedChanged(object sender, EventArgs e)
=> lensUseRelativeSize = chkSizeRelative.Checked;
private void radLensType_CheckedChanged(object sender, EventArgs e)
=> lensType = (LensType)(int.Parse((sender as Control).Tag.ToString()));
private void pctOriginal_MouseEnter(object sender, EventArgs e)
=> drawLens = true;
private void pctOriginal_MouseLeave(object sender, EventArgs e)
{
drawLens = false;
pctLens.Invalidate();
}