Как обрезать часть изображения в позиции курсора? - PullRequest
1 голос
/ 14 мая 2019

Я перехожу по ссылке , чтобы изображение было обрезано и округлено.Однако это не работает так, как я хочу.Я потратил некоторое время, но не понял, где исправить код, чтобы получить то, что я хочу.

public Image CropToCircle(Image srcImage, PointF center, float radius, Color backGround)
{
    Image dstImage = new Bitmap((int)Math.Round(Math.Ceiling(radius*2)), (int)Math.Round(Math.Ceiling(radius*2)), srcImage.PixelFormat);

    using (Graphics g = Graphics.FromImage(dstImage))
    {
        RectangleF r = new RectangleF(center.X - radius, center.Y - radius, 2*radius, 2 * radius);

        using (Brush br = new SolidBrush(backGround))
        {
            g.FillRectangle(br, 0, 0, dstImage.Width, dstImage.Height);
        }

        GraphicsPath path = new GraphicsPath();
        path.AddEllipse(r);
        g.SetClip(path);
        g.DrawImage(srcImage, 0, 0);

        return dstImage;
    }
}

dstImage - должен показывать обрезанное изображение из основного изображения в заданной позиции курсора.

Приведенный выше код работает нормально, но расположение выходного изображения перемещается с координатами X, Y.То, что я хочу, это всегда показывать квадратное изображение размером 100x100 от основного изображения под позицией курсора.(Как объектив, движущийся на изображении)

Так я называю функцию

private void drawWindows(Point mousePoint)
{               
    Image RoundedImage = CropToCircle(StartImage, new PointF(mousePoint.X, mousePoint.Y), 75, Color.FromArgb(0, 101, 167));
    PB.Image  = RoundedImage;    
}

Я хочу показать изображение в заданном месте в центре изображения следующим образом:

enter image description here

Но в настоящее время обрезанное изображение перемещается внутрь, когда я изменяю X, Y кординаты.Я хочу, чтобы круглое изображение все еще было в центре.

enter image description here

Где я совершаю ошибку?Я чувствую, что g.DrawImage(srcImage, 0, 0), вероятно, виновник.
Есть идеи?

1 Ответ

1 голос
/ 14 мая 2019

Когда вы используете 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

Полный исходный код для воспроизведения того, что показано в анимации:

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();
}
...