Possible to have anti-aliasing when drawing a clipped image?

荒凉一梦 提交于 2019-12-18 05:55:22

问题


Currently, I'm successfully using the Graphics class to draw a non-rectangular clipped image (the turtle inside):

My code looks something like:

using (var g = Graphics.FromImage(image))
{
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;

    using (var gfxPath = new GraphicsPath())
    {
        gfxPath.AddEllipse(r);

        using (var region = new Region(r))
        {
            region.Exclude(gfxPath);

            g.ExcludeClip(region);

            g.DrawImage(turtleImage, r, r2, GraphicsUnit.Pixel);
        }
    }
}

This all works just as expected. What I do not know how to solve is to make the image border anti-aliased.

The image zoomed looks like:

I.e. the border where the image ends and the transparent "background" of the image starts is a rough cut, not a smooth alpha blending.

My question is:

Is it possible to clip a drawn image and having anti-aliasing active?


回答1:


If you want to go for full blown feathering you should consider taking a look at this article:

http://danbystrom.se/2008/08/24/soft-edged-images-in-gdi/

If you want a quick and easy solution you could probably draw the image first then draw a GraphicsPath on top of it using a solid white brush with antialiasing. You would do something like this:

Rectangle outerRect = ClientRectangle;
Rectangle rect = Rectangle.Inflate(outerRect, -20, -20);

using (Image img = new Bitmap("test.jpg"))
{
    g.DrawImage(img, outerRect);

    using (SolidBrush brush = new SolidBrush(Color.White))
    using (GraphicsPath path = new GraphicsPath())
    {
        g.SmoothingMode = SmoothingMode.AntiAlias;

        path.AddEllipse(rect);
        path.AddRectangle(outerRect);

        g.FillPath(brush, path);
    }
}



回答2:


I'll like to share my solution, which is based on the selected answer. This code Resize and Crop an image into a Circle applying antialias to the edges. It also Prevents the loss of the image on Mouse Over or Window Resize. Cropped image can easily be saved.

    /// <summary>Redimensiona y recorta la imagen en forma de Circulo (con Antialias).</summary>
    /// <param name="srcImage">Imagen Original a Recortar</param>
    /// <param name="size">Tamaño deseado (en pixeles)</param>
    /// <param name="BackColor">Color de fondo</param>
    public static Image CropToCircle(System.Drawing.Image srcImage, Size size, System.Drawing.Color BackColor)
    {
        System.Drawing.Image Canvas = new System.Drawing.Bitmap(size.Width, size.Height, srcImage.PixelFormat);
        System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(Canvas);

        System.Drawing.Rectangle outerRect = new System.Drawing.Rectangle(-1, -1, Canvas.Width + 1, Canvas.Height + 1);
        System.Drawing.Rectangle rect = System.Drawing.Rectangle.Inflate(outerRect, -2, -2);

        g.DrawImage(srcImage, outerRect);

        using (System.Drawing.SolidBrush brush = new System.Drawing.SolidBrush(BackColor))
        using (System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath())
        {
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            path.AddEllipse(rect);
            path.AddRectangle(outerRect);

            g.FillPath(brush, path);
        }

        return Canvas;
    }

Usage: (Desired Size is 64x64 pix with White Background)

    System.Drawing.Image img = System.Drawing.Image.FromFile(@"E:\Mis Documentos\Mis imágenes\ergo-proxy-fullon-fight.jpg");
    System.Drawing.Image circle = Util.CropToCircle(img, new System.Drawing.Size(64,64), System.Drawing.Color.White);
    if (circle != null)
    {
        this.picUser.Image = circle;
    }



回答3:


The other answers here won't work if you want a transparent background because you cannot draw with a transparent brush - it doesn't do anything.

I found other answers that can do it (for example, using SetClip), but it doesn't come out with an anti-aliased edge.

I found this answer that works, but that one is designed to just round the corners, not make it a circle. So I modified it.

Here's how you can crop an image to a circle with a transparent background and anti-aliased edges:

/// <summary>
/// Crop the given image into a circle (or ellipse, if the image isn't square)
/// </summary>
/// <param name="img">The image to modify</param>
/// <returns>The new, round image</returns>
private static Bitmap CropCircle(Image img) {
    var roundedImage = new Bitmap(img.Width, img.Height, img.PixelFormat);

    using (var g = Graphics.FromImage(roundedImage))
    using (var gp = new GraphicsPath()) {
        g.Clear(Color.Transparent);

        g.SmoothingMode = SmoothingMode.AntiAlias;

        Brush brush = new TextureBrush(img);
        gp.AddEllipse(0, 0, img.Width, img.Height);
        g.FillPath(brush, gp);
    }

    return roundedImage;
}

The other answers draw the background color on top of the image. Instead, this creates a new, transparent image first, then draws a cut-out of the image on top.




回答4:


I had the same issue with constructing a circular profile picture with a transparent background. The tactic I finally settled on was to resize the image to an arbitrary multiple (in my case 5x), do the clipping operation, then shrink it back to the original size while using SmoothingMode.AntiAlias. I get a nicely feathered edge on the picture.

Is it a hack? Yes. Is it performant? Mmm, probably not. Does it work? Perfectly!



来源:https://stackoverflow.com/questions/19053067/possible-to-have-anti-aliasing-when-drawing-a-clipped-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!