C# - 圖片操作和Base64處理


旋轉

(1)按角度旋轉

    /// <summary>  
    /// 根據角度旋轉圖標  
    /// </summary>  
    /// <param name="img"></param>  
    public Image RotateImg(Image img, float angle)  
    {  
        //通過Png圖片設置圖片透明,修改旋轉圖片變黑問題。  
        int width = img.Width;  
        int height = img.Height;                      
        //角度  
        Matrix mtrx = new Matrix();             
        mtrx.RotateAt(angle, new PointF((width / 2), (height / 2)), MatrixOrder.Append);  
        //得到旋轉后的矩形  
        GraphicsPath path = new GraphicsPath();  
        path.AddRectangle(new RectangleF(0f, 0f, width, height));  
        RectangleF rct = path.GetBounds(mtrx);  
        //生成目標位圖  
        Bitmap devImage = new Bitmap((int)(rct.Width), (int)(rct.Height));  
        Graphics g = Graphics.FromImage(devImage);  
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;  
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;             
        //計算偏移量  
        Point Offset = new Point((int)(rct.Width - width) / 2, (int)(rct.Height - height) / 2);  
        //構造圖像顯示區域:讓圖像的中心與窗口的中心點一致  
        Rectangle rect = new Rectangle(Offset.X, Offset.Y, (int)width, (int)height);  
        Point center = new Point((int)(rect.X + rect.Width / 2), (int)(rect.Y + rect.Height / 2));              
        g.TranslateTransform(center.X, center.Y);  
        g.RotateTransform(angle);  
        //恢復圖像在水平和垂直方向的平移  
        g.TranslateTransform(-center.X, -center.Y);  
        g.DrawImage(img, rect);  
        //重至繪圖的所有變換  
        g.ResetTransform();  
        g.Save();  
        g.Dispose();  
        path.Dispose();  
        return devImage;  
    } 

(2)按弧度旋轉

    /// <summary>  
    /// 第二種方法  
    /// </summary>  
    /// <param name="b"></param>  
    /// <param name="angle"></param>  
    /// <returns></returns>  
    public Image RotateImg2(Image b, float angle)  
    {  
        angle = angle % 360;            //弧度轉換  
        double radian = angle * Math.PI / 180.0;  
        double cos = Math.Cos(radian);  
        double sin = Math.Sin(radian);  
        //原圖的寬和高  
        int w = b.Width;  
        int h = b.Height;  
        int W = (int)(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin)));  
        int H = (int)(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos)));  
        //目標位圖  
        Image dsImage = new Bitmap(W, H);  
        System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(dsImage);  
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;  
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;  
        //計算偏移量  
        Point Offset = new Point((W - w) / 2, (H - h) / 2);  
        //構造圖像顯示區域:讓圖像的中心與窗口的中心點一致  
        Rectangle rect = new Rectangle(Offset.X, Offset.Y, w, h);  
        Point center = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);  
        g.TranslateTransform(center.X, center.Y);  
        g.RotateTransform(360-angle);  
        //恢復圖像在水平和垂直方向的平移  
        g.TranslateTransform(-center.X, -center.Y);  
        g.DrawImage(b, rect);  
        //重至繪圖的所有變換  
        g.ResetTransform();  
        g.Save();  
        g.Dispose();  
        //dsImage.Save("yuancd.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);  
        return dsImage;  
    }

以上參考:基於C#的兩種圖片旋轉方法

此外,可以直接使用已封裝的方法:

/// <summary>  
/// 旋轉(利用已封裝的方法) 
/// </summary>  
/// <param name="path">圖片路徑</param>  
/// <param name="rotateFlipType">旋轉方式</param>  
/// <returns></returns>  
public bool KiRotate(string path, RotateFlipType rotateFlipType)  
{  
    try  
    {  
        using (Bitmap bitmap = new Bitmap(path))  
        {  
            // 順時針旋轉90度 RotateFlipType.Rotate90FlipNone
            // 逆時針旋轉90度 RotateFlipType.Rotate270FlipNone
            // 水平翻轉 RotateFlipType.Rotate180FlipY
            // 垂直翻轉 RotateFlipType.Rotate180FlipX
            bitmap.RotateFlip(rotateFlipType);  
            bitmap.Save(path);  
        }  
        return true;  
    }  
    catch(Exception ex)  
    {  
        return false;  
    }  
} 

轉換

(1)Bitmap & BitmapImage

// 程序集
System.Xaml.dll
System.Drawing.dll
WindowsBase.dll
PresentationCore.dll
// 命名空間
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;
using System.Windows.Interop;
using System.Windows;

/// <summary>
/// 創建一個Bitmap對象
/// </summary>
/// <param name="_uri">"../Images/test.png"</param>
public Bitmap CreatBitmapObject(string _uri)
{
    Bitmap bitmap = new Bitmap(_uri);
    return bitmap;
}

/// <summary>
/// 創建一個BitmapImage對象
/// </summary>
/// <param name="_uri">"../Images/test.png"</param>
public BitmapImage CreatBitmapImageObject(string _uri)
{
    BitmapImage bitmapImage = new BitmapImage(
        new Uri(_uri, UriKind.Relative));
    return bitmapImage;
}

/// <summary>
/// BitmapImage to Bitmap
/// </summary>
public Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
	using (MemoryStream outStream = new MemoryStream())
	{
		BitmapEncoder enc = new BmpBitmapEncoder();
		enc.Frames.Add(BitmapFrame.Create(bitmapImage));
		enc.Save(outStream);
		Bitmap bitmap = new Bitmap(outStream);
		return new Bitmap(bitmap);
	}
}

/// <summary>
/// Bitmap to BitmapImage
/// </summary>
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
public BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
{
	IntPtr hBitmap = bitmap.GetHbitmap();
	BitmapImage retval;

	try
	{
		retval = (BitmapImage)Imaging.CreateBitmapSourceFromHBitmap(
				hBitmap, IntPtr.Zero, Int32Rect.Empty,
				BitmapSizeOptions.FromEmptyOptions());
	}
	finally
	{
		DeleteObject(hBitmap);
	}

	return retval;
}

若是將Bitmap轉換成BitmapSource,只需將返回值類型更改為BitmapSource即可。

具體參考:Converting BitmapImage to Bitmap and vice versa

(2)Bitmap/BitmapImage & byte[]

/// <summary>
/// Bitmap to 字節數組
/// </summary>
public byte[] Bitmap2Bytes(Bitmap bitmap)
{
	ImageFormat format = bitmap.RawFormat;
	using (MemoryStream ms = new MemoryStream())
	{
		bitmap.Save(ms, format);
		byte[] data = new byte[ms.Length];
		//Save()會改變MemoryStream的Position,需要重新Seek到Begin也就是開始的0位置
		ms.Seek(0, SeekOrigin.Begin);
		ms.Read(data, 0, Convert.ToInt32(ms.Length));
		return data;  
	}
}
/// <summary>
/// 字節數組 to Bitmap
/// </summary>
public Bitmap Bytes2Bitmap(byte[] data)
{
	MemoryStream ms = null;
	try
	{
		ms = new MemoryStream(data);
		return new Bitmap((Image)new Bitmap(ms));
	}
	catch (Exception ex)
	{
		throw ex;   
	}
	finally
	{
		ms.Close();
	}
}

/// <summary>
/// BitmapImage to 字節數組
/// </summary>
public byte[] BitmapImage2Bytes(BitmapImage bitmapImage)
{
	byte[] bytes = null;
	try
	{
		Stream ms = bitmapImage.StreamSource;
		if (ms != null && ms.Length > 0)
		{
			//很重要,因為Position經常位於stream的末尾,導致下面讀取到的長度為0
			ms.Position = 0;
			using (BinaryReader br = new BinaryReader(ms))
			{
				bytes = br.ReadBytes((int)ms.Length);
			} 
		}
	}
	catch(Exception ex)
	{
		bytes = null;
		throw ex;
	}
	return bytes;
}
/// <summary>
/// 字節數組 to BitmapImage
/// </summary>
public BitmapImage Bytes2BitmapImage(byte[] data)
{
	BitmapImage bitmapImage = null;
	try
	{
		bitmapImage = new BitmapImage();
		bitmapImage.BeginInit();
		bitmapImage.StreamSource = new MemoryStream(data);
		bitmapImage.EndInit();
	}
	catch(Exception ex)
	{
		bitmapImage = null;
		throw ex;  
	}
	return bitmapImage;
}

此時,可以總結下Bitmap、BitmapImage、Image和BitmapSource、ImageSource之間的關系

// Image抽象類,Bitmap密封類
public abstract class Image: xxx
public sealed class Bitmap : Image
// ImageSource和BitmapSource抽象類,BitmapImage密封類 
public abstract class ImageSource: xxx
public abstract class BitmapSource : ImageSource
public sealed class BitmapImage : BitmapSource

壓縮

圖片大小 > 1M 時,可以對圖片進行壓縮,有效減小圖片大小、占用內存空間等。

/// <summary>
/// 生成縮略圖
/// </summary>
/// <param name="sourceFile">原始圖片文件</param>
/// <param name="quality">質量壓縮比:0-100,越大質量越好</param>
/// <param name="multiple">壓縮倍數</param>
/// <param name="outputFile">輸出文件名</param>
/// <returns>成功返回true,失敗返回false</returns>
/// 調用格式:GetThumImage("", 85L, 3, ""); 
public static bool GetThumImage(string sourceFile, long quality, int multiple, string outputFile)
{
	try
	{
		// 獲取圖片信息
		Bitmap sourceImage = new Bitmap(sourceFile);
		ImageCodecInfo myImageCodecInfo = GetEncoderInfo(sourceImage.RawFormat);
		if (null == myImageCodecInfo) {
			return false;
		}
		
		// 壓縮質量
		System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
		EncoderParameters myEncoderParameters = new EncoderParameters(1);
		EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, quality);
		myEncoderParameters.Param[0] = myEncoderParameter;
		
		// 按比例壓縮
		float _xWidth = sourceImage.Width;
		float _yWidth = sourceImage.Height;
		Bitmap newImage = new Bitmap((int)(_xWidth / multiple), (int)(_yWidth / multiple));
		Graphics g = Graphics.FromImage(newImage);
		/// 可按需配置屬性
		//g.CompositingQuality = CompositingQuality.HighQuality;
		//g.CompositingMode = CompositingMode.SourceCopy;
		//g.InterpolationMode = InterpolationMode.HighQualityBicubic;
		g.DrawImage(sourceImage, 0, 0, (_xWidth / multiple), (_yWidth / multiple));
		g.Dispose();

		// 保存圖片
		newImage.Save(outputFile, myImageCodecInfo, myEncoderParameters);                
		return true;
	}
	catch
	{
		return false;
	}
}

/// <summary>
/// 獲取圖片編碼信息
/// </summary>
private static ImageCodecInfo GetEncoderInfo(ImageFormat format)
{
	ImageCodecInfo[] encoders;
	encoders = ImageCodecInfo.GetImageEncoders();
	for (int j = 0; j < encoders.Length; ++j)
	{
		if (encoders[j].FormatID == format.Guid) {
			return encoders[j];
		}
	}
	return null;
}

提供一個在工作中壓縮圖片的方法

public string CompressImageString(string str) {
    string convertedImageString = string.Empty;
    byte[] b = Convert.FromBase64String(str);
    MemoryStream ms = new MemoryStream(b);
    Bitmap bitMap = new Bitmap(ms);
    bitMap = CompressImage(bitMap);
    byte[] byteArray = null;
    using (MemoryStream stream = new MemoryStream()) {
        bitMap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
        byteArray = new byte[stream.Length];
        stream.Seek(0, SeekOrigin.Begin);
        if (stream.Read(byteArray, 0, Convert.ToInt32(stream.Length)) <= 0)
        {
            return "";
        }
    }           
    convertedImageString = Convert.ToBase64String(byteArray);           
    return convertedImageString;
}
private Bitmap CompressImage(Bitmap bitmap, double compressRatio) {
    int width = (int)(bitmap.Width * compressRatio);
    int height = (int)(bitmap.Height * compressRatio);
    System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
    System.Drawing.Image myThumbnail = bitmap.GetThumbnailImage(width, height, myCallback, IntPtr.Zero);
    Bitmap compressBitmap = new Bitmap(myThumbnail);
    return compressBitmap;
}
public bool ThumbnailCallback() { return false; }

Base64處理

最常見的用於傳輸8Bit字節碼的編碼方式之一,可以將任意一組字節轉換為較長的常見文本字符序列,從而可以合法地作為首部字段值

  • Base64:一種基於64個可打印字符以及用作后綴的等號來表示二進制數據的方法
  • Base64編碼:從二進制到字符的過程,可用於在HTTP環境下傳遞較長的標識信息
  • 編碼規則:每三個8Bit的字節轉換為四個6Bit的字節(3*8 = 4*6 = 24),然后把6Bit再添兩位高位0,組成四個8Bit的字節,也就是說,轉換后的字符串理論上將要比原來的長1/3個字符變成4個字符,每76個字符加一個換行符,最后的結束符也要處理

其中,64個可打印字符包括:大小寫字母、數字、 + 和 / 。相關特點:

  • 把含有不可見字符串的信息用可見字符串表示出來、降低出錯率,但具有不可讀性、需解碼
  • 二進制序列的長度必須是24的倍數(6和8的最小公倍數)
  • 等號一定用作后綴,且數目一定是0個、1個或2個
  • 因為將3個字節轉化成4個字節,因此編碼后的文本,會比原文本大出三分之一左右
['A', 'B', 'C', 'D', ... 'a', 'b', 'c', 'd', ... '0', '1', '2', ... '+', '/']

注意,標准的Base64編碼存在 '+'和 '/',針對URL,通過"url safe"的base64編碼,將 '+'和 '/' 分別變成 '-'和 '_',同時會刪除結果最后的 '='

>>> base64.b64encode('i\xb7\x1d\xfb\xef\xff')
'abcd++//'
>>> base64.urlsafe_b64encode('i\xb7\x1d\xfb\xef\xff')
'abcd--__'

對於二進制序列長度必須是24倍數的解釋:正常情況下,只要長度是6的倍數即可。但是,當連接兩段Base64編碼過的字符串后再解碼,這個時候就需要6和8的公倍數,即長度必須是24的倍數。

現在瀏覽器已有內置的自動生成base64的方法 atob() btoa()

// 編碼
window.btoa()
// 解碼
window.atob()

具體參見:window.btoa()window.atob()

注意,待編碼字符串若包含中文,直接編碼會出現問題,應采用如下方式

function utf8_to_b64( str ) {
	return window.btoa(
		unescape(
			encodeURIComponent( str )));
}
function b64_to_utf8( str ) {
	return decodeURIComponent(
		escape(
			window.atob( str )));
} 

 

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM