我的代码打开一个图像,调整其大小,然后裁剪一个圆形区域。我想要的是更平滑的边界,因为裁剪后的图像显示出粗糙的、非抗锯齿的边缘。
图像的大小是 60x60
我曾尝试使用 Graphics.SmoothingMode
财产,但没有成功。
到目前为止,我在项目中的情况是这样的。
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;
}
要想让这个工作达到预期的效果,需要做一些改变。
不要使用... Image.Fromfile()
:如果你因为某些原因不得不这样做,总是使用过载 (string, bool
),可以保留嵌入的色彩管理信息。因为可以避免,所以采用这里所示的方法,使用 File.ReadAllBytes()
和a MemoryStream
(或 FileStream
在...中 using
块)。)
Graphics.SetClip() 不允许抗锯齿。这也适用于区域(至少不需要进一步调整)。在这里,我使用的是 TextureBrush, a 专用刷 从一个位图(您的调整后的位图)中建立,然后用它来填充椭圆,这个椭圆就是 农作物 的图像。
处理您创建的一次性对象是相当重要的,特别是当您处理图形对象时。当然,您需要处理所有您创建的一次性对象(为您提供 Dispose()
方法)。) 这包括 OpenFileDialog
对象。
不要使用这种形式的路径。@"..\..\Image.png"
:这个路径不存在(或者它不会被访问,或者只是简单的 错的),当你把你的可执行文件移到其他地方时。始终使用 Path.Combine() (如图所示)来建立一个完整的路径。这里的例子是将图像保存在一个 CroppedImages
子文件夹的可执行路径。这个主题很宽泛(例如,你可能不允许在可执行路径中存储数据,所以你可能需要在用户的 AppData
文件夹或在 ProgramData
目录)。)
所有的计算都需要 重温看看我发在这里的东西。
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);
}
}
}
的 CropToCircle
方法进行了重写,并且我添加了一个重载,允许指定一个笔的颜色。然后,钢笔将被用来在裁剪的椭圆区域周围绘制一个边框。
public static Image CropToCircle(Image srcImage, Color backColor)
{
return CropToCircle(srcImage, backColor, Color.Transparent);
}
public static Image CropToCircle(Image srcImage, Color backColor, Color penColor)
{
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(penColor, 2))
using (var g = Graphics.FromImage(cropped)) {
g.SmoothingMode = SmoothingMode.AntiAlias;
if (backColor != Color.Transparent) g.Clear(backColor);
g.FillEllipse(tBrush, rect);
if (penColor != Color.Transparent) {
rect.Inflate(-1, -1);
g.DrawEllipse(pen, rect);
}
return cropped;
}
}
这个 ResizeImage
方法进行了简化。
PixelOffsetMode.Half
. 这里的说明解释了为什么. 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;
}