using System; using System.Collections.Generic; using System.Linq; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Drawing.Imaging; namespace Disco.BI.Extensions { public static class UtilityExtensions { public static string StreamToString(this System.IO.Stream stream) { if (stream.Position != 0 && stream.CanSeek) { stream.Position = 0; } using (System.IO.StreamReader sr = new System.IO.StreamReader(stream)) { return sr.ReadToEnd(); } } #region Date/Time Extensions public static string ToFuzzy(this DateTime d) { var n = DateTime.Now; // Today if (d.Date == n.Date) { if (d < n) { // Earlier if (d > n.AddMinutes(-1)) return "A moment ago"; if (d > n.AddMinutes(-10)) return "A few minutes ago"; } else { // Later if (d < n.AddMinutes(1)) return "In a moment"; if (d < n.AddMinutes(10)) return "In a few minutes"; } return string.Format("Today at {0:h:mm tt}", d); } if (d.Date < n.Date) { // PAST var dif = n.Subtract(d); // Yesterday if (d.Date == n.Date.AddDays(-1)) return string.Format("Yesterday at {0:h:mm tt}", d); // Last Week if (dif.TotalDays <= 7) return string.Format("Last {0:dddd} at {0:h:mm tt}", d); // Within 8 Weeks if (d > n.Date.AddMonths(-2)) return string.Format("{0} Weeks ago, {1:ddd, d MMM}", (int)(dif.TotalDays / 7), d); // Same Year if (d.Year == n.Year) return string.Format("{0} Months ago, {1:ddd, d MMM}", (int)(dif.TotalDays / 30), d); } else { // Future var dif = d.Subtract(n); // Tomorrow if (d.Date == n.Date.AddDays(1)) return string.Format("Tomorrow at {0:h:mm tt}", d); // Next Week if (dif.TotalDays <= 7) return string.Format("Next {0:dddd} at {0:h:mm tt}", d); // < 2 Month if (d < n.Date.AddMonths(2)) return string.Format("In {0} Weeks, {1:ddd, d MMM}", (int)(dif.TotalDays / 7), d); // Same Year if (d.Year == n.Year) return string.Format("In {0} Months, {1:ddd, d MMM}", (int)(dif.TotalDays / 30), d); } return d.ToString("ddd, d MMM yyyy"); } public static string ToFuzzy(this DateTime? d, string NullValue = "N/A") { if (d.HasValue) return ToFuzzy(d.Value); else return NullValue; } public static string ToFullDateTime(this DateTime d) { return d.ToString("ddd, d MMM yyyy @ h:mm:sstt"); } public static string ToFullDateTime(this DateTime? d, string NullValue = "N/A") { if (d.HasValue) return ToFullDateTime(d.Value); else return NullValue; } public static long ToSortableDateTime(this DateTime? d) { if (d.HasValue) return d.Value.ToBinary(); else return -1; } public static long ToSortableDateTime(this DateTime d) { return d.ToBinary(); } #endregion #region Image Extensions public static Bitmap ResizeImage(this Image Source, int Width, int Height, Brush BackgroundColor = null) { Bitmap destination = new Bitmap(Width, Height); destination.SetResolution(72, 72); using (Graphics destinationGraphics = Graphics.FromImage(destination)) { destinationGraphics.CompositingQuality = CompositingQuality.HighQuality; destinationGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic; destinationGraphics.SmoothingMode = SmoothingMode.HighQuality; if (BackgroundColor != null) destinationGraphics.FillRectangle(BackgroundColor, destinationGraphics.VisibleClipBounds); float ratio = Math.Min((float)(destination.Width) / (float)(Source.Width), (float)(destination.Height) / (float)(Source.Height)); destinationGraphics.DrawImageResized(Source, ratio); } return destination; } public static void DrawImageResized(this Graphics graphics, Image SourceImage, float? Scale = null, float LocationX = -1, float LocationY = -1) { RectangleF clipBounds = graphics.VisibleClipBounds; if (Scale == null) // Calculate Scale Scale = Math.Min(clipBounds.Width / SourceImage.Width, clipBounds.Height / SourceImage.Height); float newWidth = SourceImage.Width * Scale.Value; float newHeight = SourceImage.Height * Scale.Value; float newLeft = LocationX; float newTop = LocationY; if (newLeft < 0 || newTop < 0) { if (newWidth < clipBounds.Width) newLeft = (clipBounds.Width - newWidth) / 2; else newLeft = 0; if (newHeight < clipBounds.Height) newTop = (clipBounds.Height - newHeight) / 2; else newTop = 0; } newLeft += clipBounds.Left; newTop += clipBounds.Top; graphics.DrawImage(SourceImage, new RectangleF(newLeft, newTop, newWidth, newHeight), new RectangleF(0, 0, SourceImage.Width, SourceImage.Height), GraphicsUnit.Pixel); } public static void EmbedIconOverlay(this Image Source, Image Icon) { int top = Math.Max(0, Source.Height - Icon.Height); int left = Math.Max(0, Source.Width - Icon.Width); using (Graphics sourceGraphics = Graphics.FromImage(Source)) { sourceGraphics.DrawImage(Icon, left, top); } } public static void SavePng(this Image Source, string Filename) { using (FileStream outStream = new FileStream(Filename, FileMode.Create, FileAccess.Write, FileShare.None)) { SavePng(Source, outStream); outStream.Flush(); outStream.Close(); } } public static void SavePng(this Image Source, Stream OutStream) { Source.Save(OutStream, ImageFormat.Png); } public static Stream SavePng(this Image Source) { MemoryStream outStream = new MemoryStream(); Source.SavePng(outStream); outStream.Position = 0; return outStream; } public static Stream SaveJpg(this Image Source, int Quality) { MemoryStream outStream = new MemoryStream(); Source.SaveJpg(Quality, outStream); outStream.Position = 0; return outStream; } public static void SaveJpg(this Image Source, int Quality, string Filename) { using (FileStream outStream = new FileStream(Filename, FileMode.Create, FileAccess.Write, FileShare.None)) { SaveJpg(Source, Quality, outStream); outStream.Flush(); outStream.Close(); } } public static void SaveJpg(this Image Source, int Quality, Stream OutStream) { ImageCodecInfo jpgCodec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType.Equals("image/jpeg", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); if (jpgCodec != null) { if (Quality < 0 || Quality > 100) throw new ArgumentOutOfRangeException("Quality", "Quality must be a positive integer <= 100"); using (EncoderParameters ep = new EncoderParameters(1)) { ep.Param[0] = new EncoderParameter(Encoder.Quality, Quality); Source.Save(OutStream, jpgCodec, ep); } } else { // Fallback Source.Save(OutStream, ImageFormat.Jpeg); } } public static ImageMontage BuildImageMontage(this IEnumerable Images, int MaxHeight = -1, int MaxWidth = -1, bool EnforceDimensions = false) { if (EnforceDimensions && (MaxHeight < 0 || MaxWidth < 0)) throw new ArgumentOutOfRangeException("EnforceDimensions", "Dimensions can only be enforced when a MaxHeight and MaxWidth is supplied"); Dictionary imageLocations = new Dictionary(); double imageScale = 1.0; int totalHeight = Images.Max(i => i.Height); int totalWidth = Images.Sum(i => i.Width); if (MaxHeight > 0 && totalHeight > MaxHeight) { imageScale = (double)MaxHeight / (double)totalHeight; } if (MaxWidth > 0 && totalWidth > MaxWidth) { imageScale = System.Math.Min(imageScale, (double)MaxWidth / (double)totalWidth); } int scaledHeight = EnforceDimensions ? MaxHeight : (int)System.Math.Round((double)totalHeight * imageScale); int scaledWidth = EnforceDimensions ? MaxWidth : (int)System.Math.Round((double)totalWidth * imageScale); System.Drawing.Bitmap imageResult = new System.Drawing.Bitmap(scaledWidth, scaledHeight); imageResult.SetResolution(72f, 72f); using (Graphics g = Graphics.FromImage(imageResult)) { g.FillRectangle(Brushes.White, new Rectangle(Point.Empty, imageResult.Size)); int imageResultNextOffset = 0; foreach (Image i in Images) { Rectangle imagePosition = new Rectangle(imageResultNextOffset, 0, (int)System.Math.Round((double)i.Width * imageScale), (int)System.Math.Round((double)i.Height * imageScale)); System.Drawing.Rectangle imageSize = new System.Drawing.Rectangle(0, 0, i.Width, i.Height); g.DrawImage(i, imagePosition, imageSize, System.Drawing.GraphicsUnit.Pixel); imageLocations[i] = imageResultNextOffset; imageResultNextOffset += imagePosition.Width; } } return new ImageMontage() { Montage = imageResult, MontageScale = imageScale, MontageSourceImageOffsets = imageLocations }; } public class ImageMontage : IDisposable { public Image Montage { get; set; } public double MontageScale { get; set; } public Dictionary MontageSourceImageOffsets { get; set; } public void Dispose() { if (Montage != null) { Montage.Dispose(); Montage = null; } if (MontageSourceImageOffsets != null) { MontageSourceImageOffsets.Clear(); MontageSourceImageOffsets = null; } } } public static Color InterpolateColours(this Color Start, Color End, double Progress) { if (Progress > 1 || Progress < 0) throw new ArgumentOutOfRangeException("Progress", "Progress must be >= 0 && <= 1"); return Color.FromArgb( (byte)(Start.A * (1 - Progress) + (End.A * Progress)), (byte)(Start.R * (1 - Progress) + (End.R * Progress)), (byte)(Start.G * (1 - Progress) + (End.G * Progress)), (byte)(Start.B * (1 - Progress) + (End.B * Progress)) ); } #endregion } }