C#中基於GDI+(Graphics)影象處理系列之高質量縮圖
阿新 • • 發佈:2019-02-20
簡介
生成圖片縮圖的功能在Web開發的工作是經常遇到的,比如使用者上傳一張圖片作為使用者頭像,根據業務的需要生成若干不同尺寸的縮圖,不同的功能使用不同尺寸的圖片等等。
大家知道Image類有一個GetThumbnailImage的方法來生成縮圖,但是這貨表現得非常不穩定,筆者就曾經掉了它的坑中被狠狠地坑了一把,所以決定自己實現生成縮圖的功能。
本文將重點向大家介紹怎麼使用GDI+(Graphics)實現生成縮圖功能,同時提醒大家要想獲得高質量的縮圖需要做的設定。
動手前先解決兩個問題
獲取高質量的Graphics
/// <summary>
/// 獲取高清的Graphics
/// </summary>
/// <param name="img"></param>
/// <returns></returns>
public Graphics GetGraphics(Image img)
{
var g = Graphics.FromImage(img);
//設定質量
g.SmoothingMode = SmoothingMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
//InterpolationMode不能使用High或者HighQualityBicubic,如果是灰色或者部分淺色的影象是會在邊緣處出一白色透明的線
//用HighQualityBilinear卻會使圖片比其他兩種模式模糊(需要肉眼仔細對比才可以看出)
g.InterpolationMode = InterpolationMode.Default;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
return g;
}
注意InterpolationMode的值為Default,之前我在設定這個值的時候,毫不猶豫的選擇了High,在實際專案中發現,對於灰色的圖片,會在圖片邊緣由於插值會畫上透明的白線,採用HighQualityBicubic也會有類似的情況;而使用HighQualityBilinear,圖片會比其他幾種模糊,當然,這種模糊要有好的顯示器和對比才能看出來,請仔細對比下面三張圖片。
儲存Image時進一步控制質量
通常我們在儲存圖片時使用下面的程式碼,這種方法就是將圖片直接儲存成jpeg格式,不能進一步控制質量,仔細觀察,在筆觸邊緣有虛化的現象。
//方法1
image. .Save("1.jpg", ImageFormat.Jpeg);
我推薦採用下面的方法
//方法2
/// <summary>
/// 將Image例項儲存到檔案,注意此方法不執行 img.Dispose()
/// 圖片儲存時本可以直接使用destImage.Save(path, ImageFormat.Jpeg),但是這種方法無法進行進一步控制圖片質量
/// </summary>
/// <param name="path"></param>
/// <param name="img"></param>
/// <param name="quality">1~100整數,無效值,則取預設值95</param>
/// <param name="mimeType"></param>
public void SaveImage2File(string path, Image destImage, int quality, string mimeType = "image/jpeg")
{
if (quality <= 0 || quality > 100) quality = 95;
//建立儲存的資料夾
FileInfo fileInfo = new FileInfo(path);
if (!Directory.Exists(fileInfo.DirectoryName))
{
Directory.CreateDirectory(fileInfo.DirectoryName);
}
//設定儲存引數,儲存引數裡進一步控制質量
EncoderParameters encoderParams = new EncoderParameters();
long[] qua = new long[] { quality };
EncoderParameter encoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
encoderParams.Param[0] = encoderParam;
//獲取指定mimeType的mimeType的ImageCodecInfo
var codecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(ici => ici.MimeType == mimeType);
destImage.Save(path, codecInfo, encoderParams);
}
使用時傳入quality值(1~100),我個人認為95已經很好了,93足夠,如採用方法1生成的圖片效果跟第二種方法quality=70生成的效果差不多。
生成高質量的縮圖
下面是生成全部原始碼,程式碼中的詳細的註釋,這裡不再贅述
/// <summary>
/// 生成高質量縮圖(固定寬高),不一定保持原寬高比
/// </summary>
/// <param name="destPath">目標儲存路徑</param>
/// <param name="srcPath">原始檔路徑</param>
/// <param name="width">生成縮圖的寬度,設定為0,則與源圖比處理</param>
/// <param name="height">生成縮圖的高度,設定為0,則與源圖等比例處理</param>
/// <param name="quality">1~100整數,無效值則取預設值95</param>
/// <param name="mimeType">如 image/jpeg</param>
public bool GetThumbnailImage(string destPath, string srcPath, int destWidth, int destHeight, int quality, out string error, string mimeType = "image/jpeg")
{
bool retVal = false;
error = string.Empty;
//寬高不能小於0
if (destWidth < 0 || destHeight < 0)
{
error = "目標寬高不能小於0";
return retVal;
}
//寬高不能同時為0
if (destWidth == 0 && destHeight == 0)
{
error = "目標寬高不能同時為0";
return retVal;
}
Image srcImage = null;
Image destImage = null;
Graphics graphics = null;
try
{
//獲取源影象
srcImage = Image.FromFile(srcPath, false);
//計算高寬比例
float d = (float)srcImage.Height / srcImage.Width;
//如果輸入的寬為0,則按高度等比縮放
if (destWidth == 0)
{
destWidth = Convert.ToInt32(destHeight / d);
}
//如果輸入的高為0,則按寬度等比縮放
if (destHeight == 0)
{
destHeight = Convert.ToInt32(destWidth * d);
}
//定義畫布
destImage = new Bitmap(destWidth, destHeight);
//獲取高清Graphics
graphics = GetGraphics(destImage);
//將源影象畫到畫布上,注意最後一個引數GraphicsUnit.Pixel
graphics.DrawImage(srcImage, new Rectangle(0, 0, destWidth, destHeight), new Rectangle(0, 0, srcImage.Width, srcImage.Height), GraphicsUnit.Pixel);
//如果是覆蓋則先釋放源資源
if (destPath == srcPath)
{
srcImage.Dispose();
}
//儲存到檔案,同時進一步控制質量
SaveImage2File(destPath, destImage, quality, mimeType);
retVal = true;
}
catch (Exception ex)
{
error = ex.Message;
}
finally
{
if (srcImage != null)
srcImage.Dispose();
if (destImage != null)
destImage.Dispose();
if (graphics != null)
graphics.Dispose();
}
return retVal;
}