My code opens an Image, resizes it, then crops a circular region.
What I want is smoother borders, since the cropped Image shows rough, non antialiased, edges.
The image size is 60x60
I have tried to use the Graphics.SmoothingMode
property but without success.
What I have so far in my project:
private void Recorte_Click(object sender, EventArgs e)
{
OpenFileDialog open = new OpenFileDialog();
// Filter for image files
open.Filter = "Image Files(*.jpg; *.jpeg; *.gif; *.bmp; *.png)|*.jpg; *.jpeg; *.gif; *.bmp; *.png";
if (open.ShowDialog() == DialogResult.OK)
{
// display image in picture box
PointF p = new PointF(1, 1);
Bitmap org = new Bitmap(open.FileName);
Image srcImage = Bitmap.FromFile(open.FileName);
// Resize image in 60x60
Image resized = ResizeImage(srcImage, new Size(60, 60), false);
MemoryStream memStream = new MemoryStream();
// Crop in round shape
Image cropped = CropToCircle(resized,Color.Transparent);
cropped.Save(@"..\..\Cortada.png", System.Drawing.Imaging.ImageFormat.Png);
pictureBox1.Image = cropped;
}
}
public static Image CropToCircle(Image srcImage, Color backGround)
{
Image dstImage = new Bitmap(srcImage.Width, srcImage.Height, srcImage.PixelFormat);
Graphics g = Graphics.FromImage(dstImage);
using (Brush br = new SolidBrush(backGround))
{
g.FillRectangle(br, 0, 0, dstImage.Width, dstImage.Height);
}
float radius = 25;
PointF center = new Point(60, 60);
GraphicsPath path = new GraphicsPath();
path.AddEllipse(7, 7, radius * 2, radius * 2);
g.SetClip(path);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawImage(srcImage, 0, 0);
return dstImage;
}
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
int newWidth;
int newHeight;
if (preserveAspectRatio)
{
int originalWidth = image.Width;
int originalHeight = image.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
Image newImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
}
return newImage;
}
Quite a few changes are required to get this working as expected:
Don't use Image.Fromfile()
: if you have to for some reason, always use the overload (string, bool
) that allows to preserve the embedded color management information. Since you can avoid it, adopt the method shown here, using File.ReadAllBytes()
and a MemoryStream
(or a FileStream
in a using
block).
Graphics.SetClip() doesn't allow anti-aliasing. This also applies to Regions (without further adjustments, at least). Here, I'm using a TextureBrush, a special brush build from a Bitmap (your resized Bitmap), which is then used to fill the ellipse that crops the resized image.
Disposing of the disposable objects you create is quite important, especially when you deal with graphics objects. Of course you need to dispose of all the disposable objects you create (objects that provide a Dispose()
method). This includes the OpenFileDialog
object.
Don't use paths in this form: @"..\..\Image.png"
: this path won't exist (or it will not be inaccessible, or simply wrong) when you move your executable somewhere else.
Always use Path.Combine() (as shown here) to build a full path.
The example here saves the Image inside a CroppedImages
subfolder in the executable path.
This topic is quite broad, though (e.g., you may not be allowed to store data in the executable path, so you may need to use a dedicated path in the User AppData
folder or in the ProgramData
directory).
All calculations need to be revisited, take a look t what I posted here.
private void Recorte_Click(object sender, EventArgs e)
{
using (var ofd = new OpenFileDialog()) {
ofd.Filter = "Image Files (*.jpg; *.jpeg; *.gif; *.bmp; *.png)|*.jpg; *.jpeg; *.gif; *.bmp; *.png";
ofd.RestoreDirectory = true;
if (ofd.ShowDialog() != DialogResult.OK) return;
Image croppedImage = null;
using (var sourceImage = Image.FromStream(new MemoryStream(File.ReadAllBytes(ofd.FileName))))
using (var resizedImage = ResizeImage(sourceImage, new Size(100, 300), false)) {
croppedImage = CropToCircle(resizedImage, Color.Transparent, Color.Turquoise);
pictureBox1.Image?.Dispose();
pictureBox1.Image = croppedImage;
string destinationPath = Path.Combine(Application.StartupPath, @"CroppedImages\Cortada.png");
croppedImage.Save(destinationPath, ImageFormat.Png);
}
}
}
The CropToCircle
method is rewritten and I've added an overload that alllows to specify a Pen Color.
The Pen will then be used to draw a border around the cropped elliptical region.
public static Image CropToCircle(Image srcImage, Color backColor)
{
return CropToCircle(srcImage, backColor, Color.Transparent);
}
public static Image CropToCircle(Image srcImage, Color backColor, Color borderColor)
{
var rect = new Rectangle(0, 0, srcImage.Width, srcImage.Height);
var cropped = new Bitmap(srcImage.Width, srcImage.Height, PixelFormat.Format32bppArgb);
using (var tBrush = new TextureBrush(srcImage))
using (var pen = new Pen(borderColor, 2))
using (var g = Graphics.FromImage(cropped)) {
g.SmoothingMode = SmoothingMode.AntiAlias;
if (backColor != Color.Transparent) g.Clear(backColor);
g.FillEllipse(tBrush, rect);
if (borderColor != Color.Transparent) {
rect.Inflate(-1, -1);
g.DrawEllipse(pen, rect);
}
return cropped;
}
}
The ResizeImage
method is simplified.
PixelOffsetMode.Half
. The notes here explain why.public static Image ResizeImage(Image image, Size newSize, bool preserveAspectRatio = true)
{
float scale = Math.Max(newSize.Width, newSize.Height) / (float)Math.Max(image.Width, image.Height);
Size imageSize = preserveAspectRatio
? Size.Round(new SizeF(image.Width * scale, image.Height * scale))
: newSize;
var resizedImage = new Bitmap(imageSize.Width, imageSize.Height);
using (var g = Graphics.FromImage(resizedImage)) {
g.PixelOffsetMode = PixelOffsetMode.Half;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, 0, 0, imageSize.Width, imageSize.Height);
}
return resizedImage;
}