Last active
December 20, 2015 13:48
-
-
Save dinowang/6141186 to your computer and use it in GitHub Desktop.
A Simple Aggregated C# Image Process API with Fluent Interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Drawing; | |
| using System.Drawing.Drawing2D; | |
| using System.Drawing.Imaging; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Threading.Tasks; | |
| namespace HexDigits.Drawing | |
| { | |
| public enum Side { Width, Height } | |
| public class ImageProcess : IDisposable | |
| { | |
| #region Static | |
| /// <summary> | |
| /// 以 空白畫布 建構圖片處理 | |
| /// </summary> | |
| /// <param name="width"></param> | |
| /// <param name="height"></param> | |
| /// <returns></returns> | |
| public static ImageProcess Handle(int width, int height) | |
| { | |
| return new ImageProcess(new Bitmap(width, height)); | |
| } | |
| /// <summary> | |
| /// 以 Bitmap 建構圖片處理 | |
| /// </summary> | |
| /// <param name="bitmap"></param> | |
| /// <returns></returns> | |
| public static ImageProcess Handle(Bitmap bitmap) | |
| { | |
| return new ImageProcess(bitmap); | |
| } | |
| /// <summary> | |
| /// 以 Stream 建構圖片處理 | |
| /// </summary> | |
| /// <param name="stream"></param> | |
| /// <returns></returns> | |
| public static ImageProcess Handle(Stream stream) | |
| { | |
| var bitmap = Image.FromStream(stream) as Bitmap; | |
| return new ImageProcess(bitmap); | |
| } | |
| /// <summary> | |
| /// 以 路徑檔名 建構圖片處理 | |
| /// </summary> | |
| /// <param name="filePath"></param> | |
| /// <returns></returns> | |
| public static ImageProcess Handle(string filePath) | |
| { | |
| if (!File.Exists(filePath)) | |
| { | |
| throw new InvalidOperationException("filePath"); | |
| } | |
| using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) | |
| { | |
| return Handle(stream); | |
| } | |
| } | |
| /// <summary> | |
| /// 儲存 Bitmap 到 Stream, 可借品質參數控制圖片品質 | |
| /// </summary> | |
| /// <param name="bitmap"></param> | |
| /// <param name="stream"></param> | |
| /// <param name="format"></param> | |
| /// <param name="quality"></param> | |
| public static void SaveAsQualityFactor(Bitmap bitmap, Stream stream, ImageFormat format = null, long? quality = null) | |
| { | |
| if (format == null) | |
| { | |
| format = ImageFormat.Jpeg; | |
| } | |
| if (quality.HasValue) | |
| { | |
| var codec = ImageCodecInfo | |
| .GetImageEncoders() | |
| .FirstOrDefault(x => x.FormatDescription.Equals(format.ToString(), StringComparison.CurrentCultureIgnoreCase)); | |
| if (codec != null) | |
| { | |
| //Set the parameters for defining the quality of the thumbnail... here it is set to 100% | |
| var encoderParameters = new EncoderParameters(1); | |
| encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality.Value); | |
| bitmap.Save(stream, codec, encoderParameters); | |
| } | |
| else | |
| { | |
| bitmap.Save(stream, format); | |
| } | |
| } | |
| else | |
| { | |
| bitmap.Save(stream, format); | |
| } | |
| } | |
| #endregion | |
| #region Private members | |
| private Action<Graphics> _graphicSetup = null; | |
| private IDictionary<string, Bitmap> _store; | |
| private Bitmap _bitmap = null; | |
| public Bitmap Bitmap | |
| { | |
| get { return _bitmap; } | |
| private set | |
| { | |
| if (_bitmap != null) | |
| { | |
| _bitmap.Dispose(); | |
| } | |
| _bitmap = value; | |
| } | |
| } | |
| public ImageFormat OutputFormat { get; private set; } | |
| public long? OutputQuality { get; private set; } | |
| #endregion | |
| #region Private methods | |
| protected void StandardGraphicsInit(Graphics graphics) | |
| { | |
| graphics.SmoothingMode = SmoothingMode.HighQuality; | |
| graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; | |
| graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; | |
| graphics.CompositingQuality = CompositingQuality.HighQuality; | |
| } | |
| #endregion | |
| /// <summary> | |
| /// 唯一建構子 | |
| /// </summary> | |
| /// <param name="bitmap"></param> | |
| private ImageProcess(Bitmap bitmap) | |
| { | |
| Bitmap = bitmap; | |
| OutputFormat = ImageFormat.Jpeg; | |
| OutputQuality = null; | |
| _graphicSetup = StandardGraphicsInit; | |
| _store = new Dictionary<string, Bitmap>(); | |
| } | |
| /// <summary> | |
| /// 回收 | |
| /// </summary> | |
| public void Dispose() | |
| { | |
| Bitmap = null; | |
| foreach (var key in _store.Keys) | |
| { | |
| _store[key].Dispose(); | |
| _store[key] = null; | |
| } | |
| _store = null; | |
| } | |
| /// <summary> | |
| /// 暫存 | |
| /// </summary> | |
| /// <param name="name"></param> | |
| /// <returns></returns> | |
| public ImageProcess Store(string name) | |
| { | |
| _store[name] = Bitmap.Clone() as Bitmap; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 提取 | |
| /// </summary> | |
| /// <param name="name"></param> | |
| /// <returns></returns> | |
| public ImageProcess Fetch(string name) | |
| { | |
| Bitmap = _store[name]; | |
| return this; | |
| } | |
| /// <summary> | |
| /// Setup | |
| /// </summary> | |
| /// <param name="g"></param> | |
| /// <returns></returns> | |
| public ImageProcess Setup(Action<Graphics> g = null) | |
| { | |
| _graphicSetup = g; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 設定輸出格式 | |
| /// </summary> | |
| /// <param name="format"></param> | |
| /// <returns></returns> | |
| public ImageProcess Format(ImageFormat format) | |
| { | |
| OutputFormat = format; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 設定輸出品質 | |
| /// </summary> | |
| /// <param name="quality"></param> | |
| /// <returns></returns> | |
| public ImageProcess Quality(long? quality) | |
| { | |
| OutputQuality = quality; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 取得尺寸 | |
| /// </summary> | |
| /// <param name="size"></param> | |
| /// <returns></returns> | |
| public ImageProcess Size(out Size size) | |
| { | |
| size = new Size(Bitmap.Width, Bitmap.Height); | |
| return this; | |
| } | |
| /// <summary> | |
| /// 取得尺寸 | |
| /// </summary> | |
| /// <param name="action"></param> | |
| /// <returns></returns> | |
| public ImageProcess Size(Action<Size> action) | |
| { | |
| var size = new Size(Bitmap.Width, Bitmap.Height); | |
| action(size); | |
| return this; | |
| } | |
| /// <summary> | |
| /// 裁切 | |
| /// </summary> | |
| /// <param name="x"></param> | |
| /// <param name="y"></param> | |
| /// <param name="width"></param> | |
| /// <param name="height"></param> | |
| /// <returns></returns> | |
| public ImageProcess Crop(int x, int y, int width, int height) | |
| { | |
| var destination = new Bitmap(width, height); | |
| using (var graphics = Graphics.FromImage(destination)) | |
| { | |
| _graphicSetup(graphics); | |
| graphics.DrawImage(Bitmap, | |
| new Rectangle(0, 0, width, height), | |
| new Rectangle(x, y, width, height), | |
| GraphicsUnit.Pixel); | |
| } | |
| Bitmap = destination; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 變更大小 | |
| /// </summary> | |
| /// <param name="length"></param> | |
| /// <param name="baseSide"></param> | |
| /// <returns></returns> | |
| public ImageProcess Resize(int length, Side baseSide = Side.Width) | |
| { | |
| var w = length; | |
| var h = length; | |
| double ratio = 0L; | |
| switch (baseSide) | |
| { | |
| case Side.Width: | |
| ratio = w / (double)Bitmap.Width; | |
| h = (int)Math.Round(Bitmap.Height * ratio); | |
| break; | |
| case Side.Height: | |
| ratio = h / (double)Bitmap.Height; | |
| w = (int)Math.Round(Bitmap.Width * ratio); | |
| break; | |
| } | |
| return Resize(w, h); | |
| } | |
| /// <summary> | |
| /// 變更大小 | |
| /// </summary> | |
| /// <param name="percent">0 ~ 1</param> | |
| /// <param name="baseSide"></param> | |
| /// <returns></returns> | |
| public ImageProcess Resize(double percent, Side baseSide = Side.Width) | |
| { | |
| var w = Bitmap.Width; | |
| var h = Bitmap.Height; | |
| double ratio = 0L; | |
| switch (baseSide) | |
| { | |
| case Side.Width: | |
| w = (int)Math.Round(w * percent); | |
| ratio = w / (double)Bitmap.Width; | |
| h = (int)Math.Round(h * ratio); | |
| break; | |
| case Side.Height: | |
| h = (int)Math.Round(h * percent); | |
| ratio = h / (double)Bitmap.Height; | |
| w = (int)Math.Round(w * ratio); | |
| break; | |
| } | |
| return Resize(w, h); | |
| } | |
| /// <summary> | |
| /// 變更大小 | |
| /// </summary> | |
| /// <param name="size"></param> | |
| /// <returns></returns> | |
| public ImageProcess Resize(Size size) | |
| { | |
| return Resize(size.Width, size.Height); | |
| } | |
| /// <summary> | |
| /// 變更大小 | |
| /// </summary> | |
| /// <param name="width"></param> | |
| /// <param name="height"></param> | |
| /// <returns></returns> | |
| public ImageProcess Resize(int width, int height) | |
| { | |
| // 寬度縮放比 | |
| var ws = (float)width / Bitmap.Width; | |
| // 高度縮放比 | |
| var hs = (float)height / Bitmap.Height; | |
| // 最佳縮放比 | |
| var ss = Math.Min(1, Math.Max(ws, hs)); | |
| // Destination | |
| var destination = new Bitmap(width, height); | |
| using (var graphics = Graphics.FromImage(destination)) | |
| { | |
| _graphicSetup(graphics); | |
| //graphics.FillRectangle(Brushes.White, 0, 0, width, height); | |
| // 最適圖寬 | |
| var _w = Bitmap.Width * ss; | |
| // 最適圖高 | |
| var _h = Bitmap.Height * ss; | |
| // 擺放位置 | |
| float x = 0; | |
| // 擺放位置 | |
| float y = 0; | |
| if (true) | |
| { | |
| // 置中 | |
| x = -(_w - (float)width) / 2; | |
| y = -(_h - (float)height) / 2; | |
| } | |
| graphics.DrawImage(Bitmap, x, y, _w, _h); | |
| } | |
| Bitmap = destination; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 透通 | |
| /// </summary> | |
| /// <param name="opacity"></param> | |
| /// <returns></returns> | |
| public ImageProcess Opacity(double opacity) | |
| { | |
| var destination = new Bitmap(Bitmap.Width, Bitmap.Height); | |
| using (var graphics = Graphics.FromImage(destination)) | |
| { | |
| _graphicSetup(graphics); | |
| //create a color matrix object | |
| var matrix = new ColorMatrix(); | |
| //set the opacity | |
| matrix.Matrix33 = (float)opacity; | |
| //create some image attributes | |
| var attributes = new ImageAttributes(); | |
| //set the color(opacity) of the image | |
| attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); | |
| var rectangle = new Rectangle(0, 0, Bitmap.Width, Bitmap.Height); | |
| graphics.DrawImage(Bitmap, rectangle, 0, 0, Bitmap.Width, Bitmap.Height, GraphicsUnit.Pixel, attributes); | |
| } | |
| Bitmap = destination; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 圖章 | |
| /// </summary> | |
| /// <param name="filePath"></param> | |
| /// <param name="stampPlacer"></param> | |
| /// <returns></returns> | |
| public ImageProcess Overlay(string filePath, Func<Size, Size, Point> stampPlacer) | |
| { | |
| return Overlay(ImageProcess.Handle(filePath), stampPlacer); | |
| } | |
| /// <summary> | |
| /// 圖章 | |
| /// </summary> | |
| /// <param name="process"></param> | |
| /// <param name="stampPlacer"></param> | |
| /// <returns></returns> | |
| public ImageProcess Overlay(ImageProcess process, Func<Size, Size, Point> stampPlacer) | |
| { | |
| var stamp = process.Bitmap; | |
| return Overlay(stamp, stampPlacer); | |
| } | |
| /// <summary> | |
| /// 圖章 | |
| /// </summary> | |
| /// <param name="stamp"></param> | |
| /// <param name="stampPlacer"></param> | |
| /// <returns></returns> | |
| public ImageProcess Overlay(Bitmap stamp, Func<Size, Size, Point> stampPlacer) | |
| { | |
| var destination = new Bitmap(Bitmap.Width, Bitmap.Height); | |
| using (var graphics = Graphics.FromImage(destination)) | |
| { | |
| _graphicSetup(graphics); | |
| graphics.DrawImage(Bitmap, 0, 0); | |
| var point = stampPlacer(new Size(Bitmap.Width, Bitmap.Height), | |
| new Size(stamp.Width, stamp.Height)); | |
| graphics.DrawImage(stamp, point); | |
| } | |
| Bitmap = destination; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 灰階 | |
| /// </summary> | |
| /// <returns></returns> | |
| public ImageProcess GrayScale() | |
| { | |
| var destination = new Bitmap(Bitmap.Width, Bitmap.Height); | |
| using (var graphics = Graphics.FromImage(destination)) | |
| { | |
| _graphicSetup(graphics); | |
| //create the grayscale ColorMatrix | |
| var colorMatrix = new ColorMatrix(new float[][] | |
| { | |
| new float[] { .3f, .3f, .3f, 0, 0 }, | |
| new float[] { .59f, .59f, .59f, 0, 0 }, | |
| new float[] { .11f, .11f, .11f, 0, 0 }, | |
| new float[] { 0, 0, 0, 1, 0 }, | |
| new float[] { 0, 0, 0, 0, 1 } | |
| }); | |
| //create some image attributes | |
| var attributes = new ImageAttributes(); | |
| //set the color matrix attribute | |
| attributes.SetColorMatrix(colorMatrix); | |
| var rectangle = new Rectangle(0, 0, Bitmap.Width, Bitmap.Height); | |
| graphics.DrawImage(Bitmap, rectangle, 0, 0, Bitmap.Width, Bitmap.Height, GraphicsUnit.Pixel, attributes); | |
| } | |
| Bitmap = destination; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 自由繪圖 | |
| /// </summary> | |
| /// <param name="g"></param> | |
| /// <returns></returns> | |
| public ImageProcess Draw(Action<Graphics, Size> g) | |
| { | |
| var destination = new Bitmap(Bitmap.Width, Bitmap.Height); | |
| using (var graphics = Graphics.FromImage(destination)) | |
| { | |
| _graphicSetup(graphics); | |
| g(graphics, new Size(Bitmap.Width, Bitmap.Height)); | |
| } | |
| Bitmap = destination; | |
| return this; | |
| } | |
| /// <summary> | |
| /// 存檔 | |
| /// </summary> | |
| /// <param name="filePath"></param> | |
| /// <returns></returns> | |
| public ImageProcess Save(string filePath) | |
| { | |
| using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite)) | |
| { | |
| return Save(stream); | |
| } | |
| } | |
| /// <summary> | |
| /// 存檔 | |
| /// </summary> | |
| /// <param name="stream"></param> | |
| /// <param name="rewind"></param> | |
| /// <returns></returns> | |
| public ImageProcess Save(Stream stream, bool rewind = false) | |
| { | |
| SaveAsQualityFactor(Bitmap, stream, OutputFormat, OutputQuality); | |
| if (rewind) | |
| { | |
| stream.Seek(0, SeekOrigin.Begin); | |
| } | |
| return this; | |
| } | |
| /// <summary> | |
| /// Delegate | |
| /// </summary> | |
| /// <param name="action"></param> | |
| /// <returns></returns> | |
| public ImageProcess TakeStream(Action<Stream> action) | |
| { | |
| using (var stream = new MemoryStream()) | |
| { | |
| Save(stream, rewind: true); | |
| action(stream); | |
| } | |
| return this; | |
| } | |
| /// <summary> | |
| /// Delegate | |
| /// </summary> | |
| /// <param name="action"></param> | |
| /// <returns></returns> | |
| public ImageProcess TakeBitmap(Action<Bitmap> action) | |
| { | |
| action(Bitmap); | |
| return this; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment