1. 程式人生 > >C#中基於GDI+(Graphics)影象處理系列之高質量縮圖

C#中基於GDI+(Graphics)影象處理系列之高質量縮圖

簡介

生成圖片縮圖的功能在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;
}

完整示例程式原始碼

示例程式截圖