如何在光标位置裁剪图像的一部分? [英] How to crop a section of an Image at cursor position?

查看:97
本文介绍了如何在光标位置裁剪图像的一部分?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在关注链接进行裁剪和四舍五入.但是,它不能按我想要的方式工作.我花了一些时间,但不知道在哪里修复代码以获得我想要的东西.

I am following the link to get the image cropped and rounded. However it does not work the way I want. I spent some time but did not understand where to fix the code to get what I want.

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 -应该在给定的光标位置显示从主图像裁剪的图像.

dstImage - should show the cropped image from main image at the given cursor position.

上面的代码工作正常,但是输出图像的位置随X,Y坐标移动.我想要的是始终在光标位置下从主图像显示100x100的正方形图像. (就像镜头在图像上移动一样)

Above code works fine but output image location moves with X,Y coordinates. What I want is always show 100x100 square image from main image under the cursor position. (Like a lens moving on the image)

这就是我调用函数的方式

This is how I call the function

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

我要在图像中心的给定位置显示图像,如下所示:

I want to show the image under given location at the center of the image as follows:

但是当我更改X,Y坐标时,当前裁剪的图像会在内部移动.我希望圆形图像仍然居中.

But currently cropped image moves inside when I change the X,Y cordinates. I want the circular image to still be at center.

我在哪里犯错?我觉得g.DrawImage(srcImage, 0, 0)可能是罪魁祸首.
有什么想法吗?

Where am I making the mistake? I feel like g.DrawImage(srcImage, 0, 0) is likely the culprit.
Any ideas?

推荐答案

当您使用控件作为图像的容器并且图像被缩放以适合容器的边界时(例如,设置

When you use a Control as the container for an Image and the Image is scaled to fit the container's bounds (e.g., setting a PictureBox.SizeMode to PictureBoxSizeMode.Zoom) so an Image can be shown in the UI with predefined measures, when you need to select a section of the Image, you need to calculate the scale factor. In other words, determine the ratio between the container's size and the Image real size.

使用较小的容器作为参考可能更好,因此您可以乘以而不是将相对度量除以比例:

It may be better to use the smaller container as reference, so you can then multiply instead of divide the relative measures by the scale ratio:

private float GetImageScaledRatio(RectangleF canvas, SizeF imageSize)
{
    return Math.Max(canvas.Width, canvas.Height) /
           Math.Max(imageSize.Width, imageSize.Height);
}

如果您希望镜头跟随鼠标指针的位置,则镜头在容器内的位置-由指针坐标减去镜头大小的一半给出:

The position of the Lens inside the container - if you want the lens to follow the Mouse pointer's position - are give by the Pointer coordinates minus half of the lens size:

private PointF GetLensPosition(PointF centerPosition, RectangleF lens)
{
    return new PointF(centerPosition.X - (lens.Width / 2), 
                      centerPosition.Y - (lens.Height / 2));
}

要确定与位图"的实际大小相关的镜头"(选择)尺寸的实际大小,当需要绘制或以其他方式裁剪位图"的一部分时,必须缩放镜头"尺寸:

To determine the actual size of the Lens (the selection) dimension in relation to the actual size of the Bitmap, the Lens dimension must be scaled when a section of a Bitmap needs to be drawn or otherwise clipped:

private SizeF GetScaledLensSize(RectangleF canvas, SizeF imageSize, SizeF lensSize)
{
    float scaleRatio = GetImageScaledRatio(canvas, imageSize);
    return new SizeF(lensSize.Width * scaleRatio, lensSize.Width * scaleRatio);
}

此外,在显示由镜头表示的当前选择的预览时,需要将选择缩放到用于预览镜头选择的容器的大小:

Also, when showing the preview of the current selection represent by the Lens, the selection needs to be scaled to the size of the Container used to preview the Lens' selection:

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

这些简单的方法既可以计算与所考虑的图像有关的选择的实际大小,也可以计算用于预览的控件的大小.

These simple methods allows to calculate both the actual size of a selection in relation to the Image considered and also the size of Controls used for the preview.

使用镜头"选区绘制预览时,最好使用一种通用方法来绘制图像"部分:该方法也可以用于在新的位图中绘制选区,然后可以将其保存光盘或以其他方式存储.

When drawing the preview using the Lens selection, it can be a good idea to use a common method to draw the Image section: a method that can also be used to draw the selection in a new Bitmap, which can then be saved to disc or otherwise stored.

在这里, pctLens 是用于预览的PictureBox, RectangleF section 是重新调整为pctLens大小(用于预览)的镜头尺寸,然后当然sourceImage是原始图像:

Here, pctLens is the PictureBox used for the preview, RectangleF section is the Lens measure rescaled to the pctLens size (for the preview) and of course sourceImage is the original Image:

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,图片框:300x175SizeMode: Zoom)

Visual result (Image: 1200x675, PictureBox: 300x175, SizeMode: Zoom)

完整的源代码可重现动画中显示的内容:

Complete source code to reproduce what is shown in the animation:

Bitmap sourceImage 是原始位图,必须将其设置为现有对象.
RectangleF imageLens 是用于定义相对镜头尺寸的形状.
Size lensPixelSize imageLens的像素大小,相对于UI表示形式.
pctOriginal 是显示原始图像的PictureBox.
pctLens 是画框,其中绘制了镜头部分预览.

Bitmap sourceImage is the original Bitmap, it must be set to an existing object.
RectangleF imageLens is the shape used to define the relative Lens size.
Size lensPixelSize is the size of imageLens in Pixels, relative to the UI representation.
pctOriginal is the PictureBox where the original Image is shown.
pctLens is the PictureBox where the Lens section preview is drawn.

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

这篇关于如何在光标位置裁剪图像的一部分?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆