From 34da140789df48aa84923a8dd33bb7acf8bfd399 Mon Sep 17 00:00:00 2001 From: Jaex Date: Sun, 18 Jun 2017 13:47:37 +0300 Subject: [PATCH] Added automatic bit depth detection for PNG encoding --- ShareX.HelpersLib/Enums.cs | 10 ++++ ShareX.HelpersLib/Helpers/ImageHelpers.cs | 8 +++ ShareX.HelpersLib/UnsafeBitmap.cs | 27 ++++++--- ShareX/TaskHelpers.cs | 73 +++++++++++++++++++---- ShareX/TaskSettings.cs | 1 + 5 files changed, 99 insertions(+), 20 deletions(-) diff --git a/ShareX.HelpersLib/Enums.cs b/ShareX.HelpersLib/Enums.cs index 7447ff036..d36ac2629 100644 --- a/ShareX.HelpersLib/Enums.cs +++ b/ShareX.HelpersLib/Enums.cs @@ -36,6 +36,16 @@ public enum EDataType URL } + public enum PNGBitDepth // TODO: Translate + { + [Description("Automatically detect")] + Automatic, + [Description("32 bit (Supports transparency)")] + Bit32, + [Description("24 bit")] + Bit24 + } + public enum GIFQuality // Localized { Default, diff --git a/ShareX.HelpersLib/Helpers/ImageHelpers.cs b/ShareX.HelpersLib/Helpers/ImageHelpers.cs index 3ae41831c..037e56a41 100644 --- a/ShareX.HelpersLib/Helpers/ImageHelpers.cs +++ b/ShareX.HelpersLib/Helpers/ImageHelpers.cs @@ -674,6 +674,14 @@ public static bool IsImagesEqual(Bitmap bmp1, Bitmap bmp2) } } + public static bool IsImageTransparent(Bitmap bmp) + { + using (UnsafeBitmap unsafeBitmap = new UnsafeBitmap(bmp, true, ImageLockMode.ReadOnly)) + { + return unsafeBitmap.IsTransparent(); + } + } + public static bool AddMetadata(Image img, int id, string text) { PropertyItem pi; diff --git a/ShareX.HelpersLib/UnsafeBitmap.cs b/ShareX.HelpersLib/UnsafeBitmap.cs index d5ebd0c43..a0a2dc4f7 100644 --- a/ShareX.HelpersLib/UnsafeBitmap.cs +++ b/ShareX.HelpersLib/UnsafeBitmap.cs @@ -36,13 +36,7 @@ public unsafe class UnsafeBitmap : IDisposable public int Width { get; private set; } public int Height { get; private set; } - public int PixelCount - { - get - { - return Width * Height; - } - } + public int PixelCount => Width * Height; private Bitmap bitmap; private BitmapData bitmapData; @@ -129,6 +123,25 @@ public static bool Compare(UnsafeBitmap bmp1, UnsafeBitmap bmp2) return true; } + public bool IsTransparent() + { + int pixelCount = PixelCount; + + ColorBgra* pointer = Pointer; + + for (int i = 0; i < pixelCount; i++) + { + if (pointer->Alpha < 255) + { + return true; + } + + pointer++; + } + + return false; + } + public ColorBgra GetPixel(int i) { return Pointer[i]; diff --git a/ShareX/TaskHelpers.cs b/ShareX/TaskHelpers.cs index b09b0a05c..e1df2c760 100644 --- a/ShareX/TaskHelpers.cs +++ b/ShareX/TaskHelpers.cs @@ -288,29 +288,22 @@ public static string CreateThumbnail(Image img, string folder, string filename, public static MemoryStream SaveImageAsStream(Image img, EImageFormat imageFormat, TaskSettings taskSettings) { - return SaveImageAsStream(img, imageFormat, taskSettings.ImageSettings.ImageJPEGQuality, taskSettings.ImageSettings.ImageGIFQuality); + return SaveImageAsStream(img, imageFormat, taskSettings.ImageSettings.ImagePNGBitDepth, + taskSettings.ImageSettings.ImageJPEGQuality, taskSettings.ImageSettings.ImageGIFQuality); } - public static MemoryStream SaveImageAsStream(Image img, EImageFormat imageFormat, int jpegQuality = 90, GIFQuality gifQuality = GIFQuality.Default) + public static MemoryStream SaveImageAsStream(Image img, EImageFormat imageFormat, PNGBitDepth pngBitDepth = PNGBitDepth.Automatic, + int jpegQuality = 90, GIFQuality gifQuality = GIFQuality.Default) { MemoryStream stream = new MemoryStream(); switch (imageFormat) { case EImageFormat.PNG: - img.Save(stream, ImageFormat.Png); + SaveImageAsPNGStream(img, stream, pngBitDepth); break; case EImageFormat.JPEG: - try - { - img = (Image)img.Clone(); - img = ImageHelpers.FillBackground(img, Color.White); - img.SaveJPG(stream, jpegQuality); - } - finally - { - if (img != null) img.Dispose(); - } + SaveImageAsJPEGStream(img, stream, jpegQuality); break; case EImageFormat.GIF: img.SaveGIF(stream, gifQuality); @@ -326,6 +319,60 @@ public static MemoryStream SaveImageAsStream(Image img, EImageFormat imageFormat return stream; } + private static void SaveImageAsPNGStream(Image img, Stream stream, PNGBitDepth bitDepth) + { + if (bitDepth == PNGBitDepth.Automatic) + { + if (ImageHelpers.IsImageTransparent((Bitmap)img)) + { + bitDepth = PNGBitDepth.Bit32; + } + else + { + bitDepth = PNGBitDepth.Bit24; + } + } + + if (bitDepth == PNGBitDepth.Bit32) + { + if (img.PixelFormat != PixelFormat.Format32bppArgb && img.PixelFormat != PixelFormat.Format32bppRgb) + { + using (Bitmap bmpNew = ((Bitmap)img).Clone(new Rectangle(0, 0, img.Width, img.Height), PixelFormat.Format32bppArgb)) + { + bmpNew.Save(stream, ImageFormat.Png); + return; + } + } + } + else if (bitDepth == PNGBitDepth.Bit24) + { + if (img.PixelFormat != PixelFormat.Format24bppRgb) + { + using (Bitmap bmpNew = ((Bitmap)img).Clone(new Rectangle(0, 0, img.Width, img.Height), PixelFormat.Format24bppRgb)) + { + bmpNew.Save(stream, ImageFormat.Png); + return; + } + } + } + + img.Save(stream, ImageFormat.Png); + } + + private static void SaveImageAsJPEGStream(Image img, Stream stream, int jpegQuality) + { + try + { + img = (Image)img.Clone(); + img = ImageHelpers.FillBackground(img, Color.White); + img.SaveJPG(stream, jpegQuality); + } + finally + { + if (img != null) img.Dispose(); + } + } + public static void SaveImageAsFile(Image img, TaskSettings taskSettings) { using (ImageData imageData = PrepareImage(img, taskSettings)) diff --git a/ShareX/TaskSettings.cs b/ShareX/TaskSettings.cs index 0acd933b9..d8e53b404 100644 --- a/ShareX/TaskSettings.cs +++ b/ShareX/TaskSettings.cs @@ -272,6 +272,7 @@ public class TaskSettingsImage #region Image / General public EImageFormat ImageFormat = EImageFormat.PNG; + public PNGBitDepth ImagePNGBitDepth = PNGBitDepth.Automatic; public int ImageJPEGQuality = 90; public GIFQuality ImageGIFQuality = GIFQuality.Default; public bool ImageAutoUseJPEG = true;