From bac075ea3955ffdbfbc488a0fcaa44d84cb1c19d Mon Sep 17 00:00:00 2001 From: tylearymf Date: Sun, 17 Jan 2021 23:50:29 +0800 Subject: [PATCH] =?UTF-8?q?Update=20=E6=94=AF=E6=8C=81=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=20PSD=E3=80=81ICO=E3=80=81WEBP=E3=80=81SVG?= =?UTF-8?q?=E3=80=81TGA=E3=80=81SVG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + SETUNA/Main/CaptureForm.cs | 30 ++- SETUNA/Main/Common/Utils.cs | 141 +++++++++-- SETUNA/Main/ScrapSourceUrl.cs | 2 +- SETUNA/Plugins/ResourceExtractor.cs | 1 - SETUNA/Plugins/WebPWrapper.cs | 378 +++++++++++++++++++++------- SETUNA/Properties/AssemblyInfo.cs | 2 +- SETUNA/SETUNA.csproj | 11 + SETUNA/packages.config | 3 + 9 files changed, 445 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index fbd7642..7da456f 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ A best screenshot small tool (support high dpi screenshots) - 支持从网站上图片拖拽创建截图 - ###### 从网站拖拽图片到某个截图中会自动创建截图(该功能需要联网,因为需要下载对应的网站图片) + +- 支持从图片格式 **JPEG**、**PNG**、**PSD** 、**GIF**、**BMP**、**ICO**、**TIFF**、**WEBP**、**SVG**、**TGA**、**SVG** 中创建截图 --- diff --git a/SETUNA/Main/CaptureForm.cs b/SETUNA/Main/CaptureForm.cs index d595a4b..7719fbe 100644 --- a/SETUNA/Main/CaptureForm.cs +++ b/SETUNA/Main/CaptureForm.cs @@ -98,12 +98,26 @@ public CaptureForm.CaptureClosedDelegate OnCaptureClose set => CaptureClosedHandler = value; } + public static Image ImgSnap + { + get => imgSnap; + set + { + if (imgSnap != null) + { + imgSnap.Dispose(); + } + + imgSnap = value; + } + } + // Token: 0x0600029C RID: 668 RVA: 0x0000DEA8 File Offset: 0x0000C0A8 public CaptureForm(SetunaOption.SetunaOptionData opt) { InitializeComponent(); targetScreen = GetCurrentScreen(); - CaptureForm.imgSnap = new Bitmap(targetScreen.Bounds.Width, targetScreen.Bounds.Height, PixelFormat.Format24bppRgb); + CaptureForm.ImgSnap = new Bitmap(targetScreen.Bounds.Width, targetScreen.Bounds.Height, PixelFormat.Format24bppRgb); CaptureForm.selArea = new Form { AutoScaleMode = AutoScaleMode.None, @@ -178,15 +192,15 @@ void InitChildForm(Form form) // Token: 0x0600029D RID: 669 RVA: 0x0000E104 File Offset: 0x0000C304 public void ShowCapture(SetunaOption.SetunaOptionData opt) { - if (CaptureForm.imgSnap == null) + if (CaptureForm.ImgSnap == null) { return; } //Cursor.Current = Cursors.Cross; targetScreen = GetCurrentScreen(); - if (targetScreen.Bounds.Width != CaptureForm.imgSnap.Width || targetScreen.Bounds.Height != CaptureForm.imgSnap.Height) + if (targetScreen.Bounds.Width != CaptureForm.ImgSnap.Width || targetScreen.Bounds.Height != CaptureForm.ImgSnap.Height) { - CaptureForm.imgSnap = new Bitmap(targetScreen.Bounds.Width, targetScreen.Bounds.Height, PixelFormat.Format24bppRgb); + CaptureForm.ImgSnap = new Bitmap(targetScreen.Bounds.Width, targetScreen.Bounds.Height, PixelFormat.Format24bppRgb); } trd = new Thread(new ThreadStart(ThreadTask)) { @@ -287,7 +301,7 @@ private Screen GetCurrentScreen() // Token: 0x0600029F RID: 671 RVA: 0x0000E680 File Offset: 0x0000C880 private void ThreadTask() { - var flag = CaptureForm.CopyFromScreen(CaptureForm.imgSnap, new Point(targetScreen.Bounds.X, targetScreen.Bounds.Y)); + var flag = CaptureForm.CopyFromScreen(CaptureForm.ImgSnap, new Point(targetScreen.Bounds.X, targetScreen.Bounds.Y)); if (flag) { base.Invoke(new CaptureForm.ShowFormDelegate(ShowForm)); @@ -491,9 +505,9 @@ private void CaptureForm_VisibleChanged(object sender, System.EventArgs e) // Token: 0x060002A8 RID: 680 RVA: 0x0000EBDF File Offset: 0x0000CDDF private void CaptureForm_Paint(object sender, PaintEventArgs e) { - if (CaptureForm.imgSnap != null) + if (CaptureForm.ImgSnap != null) { - e.Graphics.DrawImageUnscaled(CaptureForm.imgSnap, 0, 0); + e.Graphics.DrawImageUnscaled(CaptureForm.ImgSnap, 0, 0); } } @@ -700,7 +714,7 @@ private void CreateClip(Point pt, Size size) bmpClip = new Bitmap(size.Width, size.Height, PixelFormat.Format24bppRgb); using (var graphics = Graphics.FromImage(bmpClip)) { - graphics.DrawImageUnscaled(CaptureForm.imgSnap, -(pt.X - targetScreen.Bounds.X), -(pt.Y - targetScreen.Bounds.Y)); + graphics.DrawImageUnscaled(CaptureForm.ImgSnap, -(pt.X - targetScreen.Bounds.X), -(pt.Y - targetScreen.Bounds.Y)); } } diff --git a/SETUNA/Main/Common/Utils.cs b/SETUNA/Main/Common/Utils.cs index 95e609a..c4f42bb 100644 --- a/SETUNA/Main/Common/Utils.cs +++ b/SETUNA/Main/Common/Utils.cs @@ -5,6 +5,8 @@ using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; +using System.Windows.Media.Imaging; +using Svg; namespace SETUNA.Main { @@ -51,6 +53,7 @@ public static Bitmap FromPath(string path) buffer = new byte[fs.Length]; stream = new MemoryStream(buffer); fs.CopyTo(stream); + stream.Seek(0, SeekOrigin.Begin); } var imageType = ImageUtils.GetImageType(buffer); @@ -58,18 +61,40 @@ public static Bitmap FromPath(string path) { case ImageType.PNG: bitmap = new Bitmap(stream); - bitmap.MakeTransparent(Color.White); - return bitmap; + break; case ImageType.WEBP: using (var webp = new WebPWrapper.WebP()) { bitmap = webp.Decode(buffer); - return bitmap; } + break; + case ImageType.SVG: + bitmap = SvgDocument.Open(stream).Draw(); + break; + case ImageType.PSD: + var psdFile = new System.Drawing.PSD.PsdFile(); + psdFile.Load(path); + bitmap = System.Drawing.PSD.ImageDecoder.DecodeImage(psdFile); + break; + case ImageType.ICO: + using (var icon = new Icon(path)) + { + bitmap = icon.ToBitmap(); + } + break; + case ImageType.TGA: + using (var reader = new BinaryReader(stream)) + { + var image = new TgaLib.TgaImage(reader); + bitmap = image.GetBitmap().ToBitmap(); + } + break; default: bitmap = new Bitmap(stream); - return bitmap; + break; } + + return bitmap; } catch (Exception ex) { @@ -116,6 +141,19 @@ public static void DownloadImage(string url, Action finished) client.Headers[HttpRequestHeader.UserAgent] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"; client.DownloadFileAsync(new Uri(url), filePath); } + + public static Bitmap ToBitmap(this BitmapSource source) + { + Bitmap bitmap; + using (var outStream = new MemoryStream()) + { + BitmapEncoder enc = new BmpBitmapEncoder(); + enc.Frames.Add(BitmapFrame.Create(source)); + enc.Save(outStream); + bitmap = new Bitmap(outStream); + } + return bitmap; + } } static class NetUtils @@ -137,42 +175,107 @@ public static ImageType GetImageType(byte[] imageBuffer) { if (imageBuffer.Length > 3 && imageBuffer[1] == 0x50 && - imageBuffer[2] == 0x4E && - imageBuffer[3] == 0x47 - ) + imageBuffer[2] == 0x4E && + imageBuffer[3] == 0x47) { return ImageType.PNG; } else if (imageBuffer.Length > 9 && imageBuffer[6] == 0x4A && - imageBuffer[7] == 0x46 && - imageBuffer[8] == 0x49 && - imageBuffer[9] == 0x46 - ) + imageBuffer[7] == 0x46 && + imageBuffer[8] == 0x49 && + imageBuffer[9] == 0x46) { return ImageType.JPEG; } else if (imageBuffer.Length > 10 && imageBuffer[8] == 0x57 && - imageBuffer[9] == 0x45 && - imageBuffer[10] == 0x42 - ) + imageBuffer[9] == 0x45 && + imageBuffer[10] == 0x42) { return ImageType.WEBP; } else if (imageBuffer.Length > 2 && imageBuffer[0] == 0x47 && - imageBuffer[1] == 0x49 && - imageBuffer[2] == 0x46 - ) + imageBuffer[1] == 0x49 && + imageBuffer[2] == 0x46) { return ImageType.GIF; } + else if (imageBuffer.Length > 2 && + imageBuffer[1] == 0x73 && + imageBuffer[2] == 0x76 && + imageBuffer[3] == 0x67) + { + return ImageType.SVG; + } + else if (imageBuffer.Length > 2 && + imageBuffer[0] == 0x38 && + imageBuffer[1] == 0x42 && + imageBuffer[2] == 0x50 && + imageBuffer[3] == 0x53) + { + return ImageType.PSD; + } + else if (imageBuffer.Length > 5 && + imageBuffer[0] == 0x00 && + imageBuffer[1] == 0x00 && + imageBuffer[2] == 0x01 && + imageBuffer[3] == 0x00 && + imageBuffer[4] == 0x01 && + imageBuffer[5] == 0x00) + { + return ImageType.ICO; + } + else if (TGAUtils.IsTGA(imageBuffer)) + { + return ImageType.TGA; + } return ImageType.Unknown; } } + static class TGAUtils + { + public static bool IsTGA(byte[] imageBuffer) + { + if (imageBuffer.Length > 16) + { + var imageType = imageBuffer[2]; + var colorMapDepth = imageBuffer[7]; + var pixelDepth = imageBuffer[16]; + + switch (imageType) + { + case 1: + case 9: + if (colorMapDepth - 15 <= 1 || colorMapDepth == 24 || colorMapDepth == 32) + { + return true; + } + break; + case 2: + case 10: + if (pixelDepth - 15 <= 1 || pixelDepth == 24 || pixelDepth == 32) + { + return true; + } + break; + case 3: + case 11: + if (pixelDepth == 8) + { + return true; + } + break; + } + } + + return false; + } + } + public enum ImageType { Unknown, @@ -180,6 +283,10 @@ public enum ImageType PNG, WEBP, GIF, + SVG, + PSD, + ICO, + TGA, } } diff --git a/SETUNA/Main/ScrapSourceUrl.cs b/SETUNA/Main/ScrapSourceUrl.cs index 0ecd575..00d9a84 100644 --- a/SETUNA/Main/ScrapSourceUrl.cs +++ b/SETUNA/Main/ScrapSourceUrl.cs @@ -31,7 +31,7 @@ public override Image GetImage() public override Point GetPosition() { - return point; + return point; } // Token: 0x0400010F RID: 271 diff --git a/SETUNA/Plugins/ResourceExtractor.cs b/SETUNA/Plugins/ResourceExtractor.cs index 0c37cfb..599e1cd 100644 --- a/SETUNA/Plugins/ResourceExtractor.cs +++ b/SETUNA/Plugins/ResourceExtractor.cs @@ -1,5 +1,4 @@ using System; -using System.IO; namespace SETUNA.Plugins { diff --git a/SETUNA/Plugins/WebPWrapper.cs b/SETUNA/Plugins/WebPWrapper.cs index e9b0546..69ae97d 100644 --- a/SETUNA/Plugins/WebPWrapper.cs +++ b/SETUNA/Plugins/WebPWrapper.cs @@ -48,7 +48,7 @@ public Bitmap Load(string pathFileName) { try { - byte[] rawWebP = File.ReadAllBytes(pathFileName); + var rawWebP = File.ReadAllBytes(pathFileName); return Decode(rawWebP); } @@ -62,30 +62,42 @@ public Bitmap Decode(byte[] rawWebP) { Bitmap bmp = null; BitmapData bmpData = null; - GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); + var pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); int size; try { //Get image width and height - GetInfo(rawWebP, out int imgWidth, out int imgHeight, out bool hasAlpha, out bool hasAnimation, out string format); + GetInfo(rawWebP, out var imgWidth, out var imgHeight, out var hasAlpha, out var hasAnimation, out var format); //Create a BitmapData and Lock all pixels to be written if (hasAlpha) + { bmp = new Bitmap(imgWidth, imgHeight, PixelFormat.Format32bppArgb); + } else + { bmp = new Bitmap(imgWidth, imgHeight, PixelFormat.Format24bppRgb); + } + bmpData = bmp.LockBits(new Rectangle(0, 0, imgWidth, imgHeight), ImageLockMode.WriteOnly, bmp.PixelFormat); //Uncompress the image - int outputSize = bmpData.Stride * imgHeight; - IntPtr ptrData = pinnedWebP.AddrOfPinnedObject(); + var outputSize = bmpData.Stride * imgHeight; + var ptrData = pinnedWebP.AddrOfPinnedObject(); if (bmp.PixelFormat == PixelFormat.Format24bppRgb) + { size = UnsafeNativeMethods.WebPDecodeBGRInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride); + } else + { size = UnsafeNativeMethods.WebPDecodeBGRAInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride); + } + if (size == 0) + { throw new Exception("Can´t encode WebP"); + } return bmp; } @@ -94,11 +106,15 @@ public Bitmap Decode(byte[] rawWebP) { //Unlock the pixels if (bmpData != null) + { bmp.UnlockBits(bmpData); + } //Free memory if (pinnedWebP.IsAllocated) + { pinnedWebP.Free(); + } } } @@ -108,32 +124,37 @@ public Bitmap Decode(byte[] rawWebP) /// Bitmap with the WebP image public Bitmap Decode(byte[] rawWebP, WebPDecoderOptions options) { - GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); + var pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); Bitmap bmp = null; BitmapData bmpData = null; VP8StatusCode result; try { - WebPDecoderConfig config = new WebPDecoderConfig(); + var config = new WebPDecoderConfig(); if (UnsafeNativeMethods.WebPInitDecoderConfig(ref config) == 0) { throw new Exception("WebPInitDecoderConfig failed. Wrong version?"); } // Read the .webp input file information - IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); + var ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); int height; int width; if (options.use_scaling == 0) { result = UnsafeNativeMethods.WebPGetFeatures(ptrRawWebP, rawWebP.Length, ref config.input); if (result != VP8StatusCode.VP8_STATUS_OK) + { throw new Exception("Failed WebPGetFeatures with error " + result); + } //Test cropping values if (options.use_cropping == 1) { if (options.crop_left + options.crop_width > config.input.Width || options.crop_top + options.crop_height > config.input.Height) + { throw new Exception("Crop options exceded WebP image dimensions"); + } + width = options.crop_width; height = options.crop_height; } @@ -195,11 +216,15 @@ public Bitmap Decode(byte[] rawWebP, WebPDecoderOptions options) { //Unlock the pixels if (bmpData != null) + { bmp.UnlockBits(bmpData); + } //Free memory if (pinnedWebP.IsAllocated) + { pinnedWebP.Free(); + } } } @@ -210,15 +235,17 @@ public Bitmap Decode(byte[] rawWebP, WebPDecoderOptions options) /// Bitmap with the WebP thumbnail in 24bpp public Bitmap GetThumbnailFast(byte[] rawWebP, int width, int height) { - GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); + var pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); Bitmap bmp = null; BitmapData bmpData = null; try { - WebPDecoderConfig config = new WebPDecoderConfig(); + var config = new WebPDecoderConfig(); if (UnsafeNativeMethods.WebPInitDecoderConfig(ref config) == 0) + { throw new Exception("WebPInitDecoderConfig failed. Wrong version?"); + } // Set up decode options config.options.bypass_filtering = 1; @@ -242,10 +269,12 @@ public Bitmap GetThumbnailFast(byte[] rawWebP, int width, int height) config.output.is_external_memory = 1; // Decode - IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); - VP8StatusCode result = UnsafeNativeMethods.WebPDecode(ptrRawWebP, rawWebP.Length, ref config); + var ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); + var result = UnsafeNativeMethods.WebPDecode(ptrRawWebP, rawWebP.Length, ref config); if (result != VP8StatusCode.VP8_STATUS_OK) + { throw new Exception("Failed WebPDecode with error " + result); + } UnsafeNativeMethods.WebPFreeDecBuffer(ref config.output); @@ -256,11 +285,15 @@ public Bitmap GetThumbnailFast(byte[] rawWebP, int width, int height) { //Unlock the pixels if (bmpData != null) + { bmp.UnlockBits(bmpData); + } //Free memory if (pinnedWebP.IsAllocated) + { pinnedWebP.Free(); + } } } @@ -271,20 +304,24 @@ public Bitmap GetThumbnailFast(byte[] rawWebP, int width, int height) /// Bitmap with the WebP thumbnail public Bitmap GetThumbnailQuality(byte[] rawWebP, int width, int height) { - GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); + var pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); Bitmap bmp = null; BitmapData bmpData = null; try { - WebPDecoderConfig config = new WebPDecoderConfig(); + var config = new WebPDecoderConfig(); if (UnsafeNativeMethods.WebPInitDecoderConfig(ref config) == 0) + { throw new Exception("WebPInitDecoderConfig failed. Wrong version?"); + } - IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); - VP8StatusCode result = UnsafeNativeMethods.WebPGetFeatures(ptrRawWebP, rawWebP.Length, ref config.input); - if (result != VP8StatusCode.VP8_STATUS_OK) - throw new Exception("Failed WebPGetFeatures with error " + result); + var ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); + var result = UnsafeNativeMethods.WebPGetFeatures(ptrRawWebP, rawWebP.Length, ref config.input); + if (result != VP8StatusCode.VP8_STATUS_OK) + { + throw new Exception("Failed WebPGetFeatures with error " + result); + } // Set up decode options config.options.bypass_filtering = 0; @@ -318,7 +355,9 @@ public Bitmap GetThumbnailQuality(byte[] rawWebP, int width, int height) // Decode result = UnsafeNativeMethods.WebPDecode(ptrRawWebP, rawWebP.Length, ref config); if (result != VP8StatusCode.VP8_STATUS_OK) + { throw new Exception("Failed WebPDecode with error " + result); + } UnsafeNativeMethods.WebPFreeDecBuffer(ref config.output); @@ -329,11 +368,15 @@ public Bitmap GetThumbnailQuality(byte[] rawWebP, int width, int height) { //Unlock the pixels if (bmpData != null) + { bmp.UnlockBits(bmpData); + } //Free memory if (pinnedWebP.IsAllocated) + { pinnedWebP.Free(); + } } } #endregion @@ -366,14 +409,22 @@ public byte[] EncodeLossy(Bitmap bmp, int quality = 75) { //test bmp if (bmp.Width == 0 || bmp.Height == 0) + { throw new ArgumentException("Bitmap contains no data.", "bmp"); + } + if (bmp.Width > WEBP_MAX_DIMENSION || bmp.Height > WEBP_MAX_DIMENSION) + { throw new NotSupportedException("Bitmap's dimension is too large. Max is " + WEBP_MAX_DIMENSION + "x" + WEBP_MAX_DIMENSION + " pixels."); + } + if (bmp.PixelFormat != PixelFormat.Format24bppRgb && bmp.PixelFormat != PixelFormat.Format32bppArgb) + { throw new NotSupportedException("Only support Format24bppRgb and Format32bppArgb pixelFormat."); + } BitmapData bmpData = null; - IntPtr unmanagedData = IntPtr.Zero; + var unmanagedData = IntPtr.Zero; int size; try @@ -383,14 +434,21 @@ public byte[] EncodeLossy(Bitmap bmp, int quality = 75) //Compress the bmp data if (bmp.PixelFormat == PixelFormat.Format24bppRgb) + { size = UnsafeNativeMethods.WebPEncodeBGR(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, quality, out unmanagedData); + } else + { size = UnsafeNativeMethods.WebPEncodeBGRA(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, quality, out unmanagedData); + } + if (size == 0) + { throw new Exception("Can´t encode WebP"); + } //Copy image compress data to output array - byte[] rawWebP = new byte[size]; + var rawWebP = new byte[size]; Marshal.Copy(unmanagedData, rawWebP, 0, size); return rawWebP; @@ -400,11 +458,15 @@ public byte[] EncodeLossy(Bitmap bmp, int quality = 75) { //Unlock the pixels if (bmpData != null) + { bmp.UnlockBits(bmpData); + } //Free memory if (unmanagedData != IntPtr.Zero) + { UnsafeNativeMethods.WebPFree(unmanagedData); + } } } @@ -416,16 +478,21 @@ public byte[] EncodeLossy(Bitmap bmp, int quality = 75) public byte[] EncodeLossy(Bitmap bmp, int quality, int speed, bool info = false) { //Inicialize config struct - WebPConfig config = new WebPConfig(); + var config = new WebPConfig(); //Set compresion parameters if (UnsafeNativeMethods.WebPConfigInit(ref config, WebPPreset.WEBP_PRESET_DEFAULT, 75) == 0) + { throw new Exception("Can´t config preset"); + } // Add additional tuning: config.method = speed; if (config.method > 6) + { config.method = 6; + } + config.quality = quality; config.autofilter = 1; config.pass = speed + 1; @@ -442,7 +509,9 @@ public byte[] EncodeLossy(Bitmap bmp, int quality, int speed, bool info = false) config.use_sharp_yuv = 1; } else + { config.preprocessing = 3; + } return AdvancedEncode(bmp, config, info); } @@ -454,14 +523,22 @@ public byte[] EncodeLossless(Bitmap bmp) { //test bmp if (bmp.Width == 0 || bmp.Height == 0) + { throw new ArgumentException("Bitmap contains no data.", "bmp"); + } + if (bmp.Width > WEBP_MAX_DIMENSION || bmp.Height > WEBP_MAX_DIMENSION) + { throw new NotSupportedException("Bitmap's dimension is too large. Max is " + WEBP_MAX_DIMENSION + "x" + WEBP_MAX_DIMENSION + " pixels."); + } + if (bmp.PixelFormat != PixelFormat.Format24bppRgb && bmp.PixelFormat != PixelFormat.Format32bppArgb) + { throw new NotSupportedException("Only support Format24bppRgb and Format32bppArgb pixelFormat."); + } BitmapData bmpData = null; - IntPtr unmanagedData = IntPtr.Zero; + var unmanagedData = IntPtr.Zero; try { //Get bmp data @@ -470,12 +547,16 @@ public byte[] EncodeLossless(Bitmap bmp) //Compress the bmp data int size; if (bmp.PixelFormat == PixelFormat.Format24bppRgb) + { size = UnsafeNativeMethods.WebPEncodeLosslessBGR(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, out unmanagedData); + } else + { size = UnsafeNativeMethods.WebPEncodeLosslessBGRA(bmpData.Scan0, bmp.Width, bmp.Height, bmpData.Stride, out unmanagedData); + } //Copy image compress data to output array - byte[] rawWebP = new byte[size]; + var rawWebP = new byte[size]; Marshal.Copy(unmanagedData, rawWebP, 0, size); return rawWebP; @@ -485,11 +566,15 @@ public byte[] EncodeLossless(Bitmap bmp) { //Unlock the pixels if (bmpData != null) + { bmp.UnlockBits(bmpData); + } //Free memory if (unmanagedData != IntPtr.Zero) + { UnsafeNativeMethods.WebPFree(unmanagedData); + } } } @@ -500,24 +585,31 @@ public byte[] EncodeLossless(Bitmap bmp) public byte[] EncodeLossless(Bitmap bmp, int speed) { //Inicialize config struct - WebPConfig config = new WebPConfig(); + var config = new WebPConfig(); //Set compresion parameters if (UnsafeNativeMethods.WebPConfigInit(ref config, WebPPreset.WEBP_PRESET_DEFAULT, (speed + 1) * 10) == 0) + { throw new Exception("Can´t config preset"); + } //Old version of dll not suport info and WebPConfigLosslessPreset if (UnsafeNativeMethods.WebPGetDecoderVersion() > 1082) { if (UnsafeNativeMethods.WebPConfigLosslessPreset(ref config, speed) == 0) + { throw new Exception("Can´t config lossless preset"); + } } else { config.lossless = 1; config.method = speed; if (config.method > 6) + { config.method = 6; + } + config.quality = (speed + 1) * 10; } config.pass = speed + 1; @@ -538,16 +630,24 @@ public byte[] EncodeNearLossless(Bitmap bmp, int quality, int speed = 9) { //test dll version if (UnsafeNativeMethods.WebPGetDecoderVersion() <= 1082) + { throw new Exception("This dll version not suport EncodeNearLossless"); + } //Inicialize config struct - WebPConfig config = new WebPConfig(); + var config = new WebPConfig(); //Set compresion parameters if (UnsafeNativeMethods.WebPConfigInit(ref config, WebPPreset.WEBP_PRESET_DEFAULT, (speed + 1) * 10) == 0) + { throw new Exception("Can´t config preset"); + } + if (UnsafeNativeMethods.WebPConfigLosslessPreset(ref config, speed) == 0) + { throw new Exception("Can´t config lossless preset"); + } + config.pass = speed + 1; config.near_lossless = quality; config.thread_level = 1; @@ -566,7 +666,7 @@ public string GetVersion() { try { - uint v = (uint)UnsafeNativeMethods.WebPGetDecoderVersion(); + var v = (uint)UnsafeNativeMethods.WebPGetDecoderVersion(); var revision = v % 256; var minor = (v >> 8) % 256; var major = (v >> 16) % 256; @@ -585,22 +685,40 @@ public string GetVersion() public void GetInfo(byte[] rawWebP, out int width, out int height, out bool has_alpha, out bool has_animation, out string format) { VP8StatusCode result; - GCHandle pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); + var pinnedWebP = GCHandle.Alloc(rawWebP, GCHandleType.Pinned); try { - IntPtr ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); + var ptrRawWebP = pinnedWebP.AddrOfPinnedObject(); - WebPBitstreamFeatures features = new WebPBitstreamFeatures(); + var features = new WebPBitstreamFeatures(); result = UnsafeNativeMethods.WebPGetFeatures(ptrRawWebP, rawWebP.Length, ref features); if (result != 0) + { throw new Exception(result.ToString()); + } width = features.Width; height = features.Height; - if (features.Has_alpha == 1) has_alpha = true; else has_alpha = false; - if (features.Has_animation == 1) has_animation = true; else has_animation = false; + if (features.Has_alpha == 1) + { + has_alpha = true; + } + else + { + has_alpha = false; + } + + if (features.Has_animation == 1) + { + has_animation = true; + } + else + { + has_animation = false; + } + switch (features.Format) { case 1: @@ -619,7 +737,9 @@ public void GetInfo(byte[] rawWebP, out int width, out int height, out bool has_ { //Free memory if (pinnedWebP.IsAllocated) + { pinnedWebP.Free(); + } } } @@ -630,53 +750,74 @@ public void GetInfo(byte[] rawWebP, out int width, out int height, out bool has_ /// dB in the Y/U/V/Alpha/All order public float[] GetPictureDistortion(Bitmap source, Bitmap reference, int metric_type) { - WebPPicture wpicSource = new WebPPicture(); - WebPPicture wpicReference = new WebPPicture(); + var wpicSource = new WebPPicture(); + var wpicReference = new WebPPicture(); BitmapData sourceBmpData = null; BitmapData referenceBmpData = null; - float[] result = new float[5]; - GCHandle pinnedResult = GCHandle.Alloc(result, GCHandleType.Pinned); + var result = new float[5]; + var pinnedResult = GCHandle.Alloc(result, GCHandleType.Pinned); try { if (source == null) + { throw new Exception("Source picture is void"); + } + if (reference == null) + { throw new Exception("Reference picture is void"); + } + if (metric_type > 2) + { throw new Exception("Bad metric_type. Use 0 = PSNR, 1 = SSIM, 2 = LSIM"); + } + if (source.Width != reference.Width || source.Height != reference.Height) + { throw new Exception("Source and Reference pictures have diferent dimensions"); + } // Setup the source picture data, allocating the bitmap, width and height sourceBmpData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, source.PixelFormat); wpicSource = new WebPPicture(); if (UnsafeNativeMethods.WebPPictureInitInternal(ref wpicSource) != 1) + { throw new Exception("Can´t init WebPPictureInit"); - wpicSource.width = (int)source.Width; - wpicSource.height = (int)source.Height; + } + + wpicSource.width = source.Width; + wpicSource.height = source.Height; //Put the source bitmap componets in wpic if (sourceBmpData.PixelFormat == PixelFormat.Format32bppArgb) { wpicSource.use_argb = 1; if (UnsafeNativeMethods.WebPPictureImportBGRA(ref wpicSource, sourceBmpData.Scan0, sourceBmpData.Stride) != 1) + { throw new Exception("Can´t allocate memory in WebPPictureImportBGR"); + } } else { wpicSource.use_argb = 0; if (UnsafeNativeMethods.WebPPictureImportBGR(ref wpicSource, sourceBmpData.Scan0, sourceBmpData.Stride) != 1) + { throw new Exception("Can´t allocate memory in WebPPictureImportBGR"); + } } // Setup the reference picture data, allocating the bitmap, width and height referenceBmpData = reference.LockBits(new Rectangle(0, 0, reference.Width, reference.Height), ImageLockMode.ReadOnly, reference.PixelFormat); wpicReference = new WebPPicture(); if (UnsafeNativeMethods.WebPPictureInitInternal(ref wpicReference) != 1) + { throw new Exception("Can´t init WebPPictureInit"); - wpicReference.width = (int)reference.Width; - wpicReference.height = (int)reference.Height; + } + + wpicReference.width = reference.Width; + wpicReference.height = reference.Height; wpicReference.use_argb = 1; //Put the source bitmap componets in wpic @@ -684,19 +825,26 @@ public float[] GetPictureDistortion(Bitmap source, Bitmap reference, int metric_ { wpicSource.use_argb = 1; if (UnsafeNativeMethods.WebPPictureImportBGRA(ref wpicReference, referenceBmpData.Scan0, referenceBmpData.Stride) != 1) + { throw new Exception("Can´t allocate memory in WebPPictureImportBGR"); + } } else { wpicSource.use_argb = 0; if (UnsafeNativeMethods.WebPPictureImportBGR(ref wpicReference, referenceBmpData.Scan0, referenceBmpData.Stride) != 1) + { throw new Exception("Can´t allocate memory in WebPPictureImportBGR"); + } } //Measure - IntPtr ptrResult = pinnedResult.AddrOfPinnedObject(); + var ptrResult = pinnedResult.AddrOfPinnedObject(); if (UnsafeNativeMethods.WebPPictureDistortion(ref wpicSource, ref wpicReference, metric_type, ptrResult) != 1) + { throw new Exception("Can´t measure."); + } + return result; } catch (Exception ex) { throw new Exception(ex.Message + "\r\nIn WebP.GetPictureDistortion"); } @@ -704,18 +852,30 @@ public float[] GetPictureDistortion(Bitmap source, Bitmap reference, int metric_ { //Unlock the pixels if (sourceBmpData != null) + { source.UnlockBits(sourceBmpData); + } + if (referenceBmpData != null) + { reference.UnlockBits(referenceBmpData); + } //Free memory if (wpicSource.argb != IntPtr.Zero) + { UnsafeNativeMethods.WebPPictureFree(ref wpicSource); + } + if (wpicReference.argb != IntPtr.Zero) + { UnsafeNativeMethods.WebPPictureFree(ref wpicReference); + } //Free memory if (pinnedResult.IsAllocated) + { pinnedResult.Free(); + } } } #endregion @@ -730,40 +890,56 @@ private byte[] AdvancedEncode(Bitmap bmp, WebPConfig config, bool info) { byte[] rawWebP = null; byte[] dataWebp = null; - WebPPicture wpic = new WebPPicture(); + var wpic = new WebPPicture(); BitmapData bmpData = null; - WebPAuxStats stats = new WebPAuxStats(); - IntPtr ptrStats = IntPtr.Zero; - GCHandle pinnedArrayHandle = new GCHandle(); + var stats = new WebPAuxStats(); + var ptrStats = IntPtr.Zero; + var pinnedArrayHandle = new GCHandle(); int dataWebpSize; try { //Validate the config if (UnsafeNativeMethods.WebPValidateConfig(ref config) != 1) + { throw new Exception("Bad config parameters"); + } //test bmp if (bmp.Width == 0 || bmp.Height == 0) + { throw new ArgumentException("Bitmap contains no data.", "bmp"); + } + if (bmp.Width > WEBP_MAX_DIMENSION || bmp.Height > WEBP_MAX_DIMENSION) + { throw new NotSupportedException("Bitmap's dimension is too large. Max is " + WEBP_MAX_DIMENSION + "x" + WEBP_MAX_DIMENSION + " pixels."); + } + if (bmp.PixelFormat != PixelFormat.Format24bppRgb && bmp.PixelFormat != PixelFormat.Format32bppArgb) + { throw new NotSupportedException("Only support Format24bppRgb and Format32bppArgb pixelFormat."); + } // Setup the input data, allocating a the bitmap, width and height bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); if (UnsafeNativeMethods.WebPPictureInitInternal(ref wpic) != 1) + { throw new Exception("Can´t init WebPPictureInit"); - wpic.width = (int)bmp.Width; - wpic.height = (int)bmp.Height; + } + + wpic.width = bmp.Width; + wpic.height = bmp.Height; wpic.use_argb = 1; if (bmp.PixelFormat == PixelFormat.Format32bppArgb) { //Put the bitmap componets in wpic - int result = UnsafeNativeMethods.WebPPictureImportBGRA(ref wpic, bmpData.Scan0, bmpData.Stride); + var result = UnsafeNativeMethods.WebPPictureImportBGRA(ref wpic, bmpData.Scan0, bmpData.Stride); if (result != 1) + { throw new Exception("Can´t allocate memory in WebPPictureImportBGRA"); + } + wpic.colorspace = (uint)WEBP_CSP_MODE.MODE_bgrA; dataWebpSize = bmp.Width * bmp.Height * 32; dataWebp = new byte[bmp.Width * bmp.Height * 32]; //Memory for WebP output @@ -771,9 +947,12 @@ private byte[] AdvancedEncode(Bitmap bmp, WebPConfig config, bool info) else { //Put the bitmap componets in wpic - int result = UnsafeNativeMethods.WebPPictureImportBGR(ref wpic, bmpData.Scan0, bmpData.Stride); + var result = UnsafeNativeMethods.WebPPictureImportBGR(ref wpic, bmpData.Scan0, bmpData.Stride); if (result != 1) + { throw new Exception("Can´t allocate memory in WebPPictureImportBGR"); + } + dataWebpSize = bmp.Width * bmp.Height * 24; } @@ -790,10 +969,13 @@ private byte[] AdvancedEncode(Bitmap bmp, WebPConfig config, bool info) //Memory for WebP output if (dataWebpSize > 2147483591) + { dataWebpSize = 2147483591; - dataWebp = new byte[bmp.Width * bmp.Height * 32]; + } + + dataWebp = new byte[bmp.Width * bmp.Height * 32]; pinnedArrayHandle = GCHandle.Alloc(dataWebp, GCHandleType.Pinned); - IntPtr initPtr = pinnedArrayHandle.AddrOfPinnedObject(); + var initPtr = pinnedArrayHandle.AddrOfPinnedObject(); wpic.custom_ptr = initPtr; // Set up a byte-writing method (write-to-memory, in this case) @@ -802,7 +984,9 @@ private byte[] AdvancedEncode(Bitmap bmp, WebPConfig config, bool info) //compress the input samples if (UnsafeNativeMethods.WebPEncode(ref config, ref wpic) != 1) + { throw new Exception("Encoding error: " + ((WebPEncodingError)wpic.error_code).ToString()); + } //Remove OnCallback UnsafeNativeMethods.OnCallback = null; @@ -812,7 +996,7 @@ private byte[] AdvancedEncode(Bitmap bmp, WebPConfig config, bool info) bmpData = null; //Copy webpData to rawWebP - int size = (int)((long)wpic.custom_ptr - (long)initPtr); + var size = (int)((long)wpic.custom_ptr - (long)initPtr); rawWebP = new byte[size]; Array.Copy(dataWebp, rawWebP, size); @@ -1604,7 +1788,7 @@ public struct WebPConfig /// If non-zero, specifies the minimal distortion to try to achieve. Takes precedence over target_size. public float target_PSNR; /// Maximum number of segments to use, in [1..4] - public int segments; + public int segments; /// Spatial Noise Shaping. 0=off, 100=maximum. public int sns_strength; /// Range: [0 = off .. 100 = strongest] @@ -1614,31 +1798,31 @@ public struct WebPConfig /// Filtering type: 0 = simple, 1 = strong (only used if filter_strength > 0 or autofilter > 0) public int filter_type; /// Auto adjust filter's strength [0 = off, 1 = on] - public int autofilter; + public int autofilter; /// Algorithm for encoding the alpha plane (0 = none, 1 = compressed with WebP lossless). Default is 1. public int alpha_compression; /// Predictive filtering method for alpha plane. 0: none, 1: fast, 2: best. Default if 1. - public int alpha_filtering; + public int alpha_filtering; /// Between 0 (smallest size) and 100 (lossless). Default is 100. - public int alpha_quality; + public int alpha_quality; /// Number of entropy-analysis passes (in [1..10]). - public int pass; + public int pass; /// If true, export the compressed picture back. In-loop filtering is not applied. public int show_compressed; /// Preprocessing filter (0=none, 1=segment-smooth, 2=pseudo-random dithering) - public int preprocessing; + public int preprocessing; /// Log2(number of token partitions) in [0..3] Default is set to 0 for easier progressive decoding. - public int partitions; + public int partitions; /// Quality degradation allowed to fit the 512k limit on prediction modes coding (0: no degradation, 100: maximum possible degradation). public int partition_limit; /// If true, compression parameters will be remapped to better match the expected output size from JPEG compression. Generally, the output size will be similar but the degradation will be lower. - public int emulate_jpeg_size; + public int emulate_jpeg_size; /// If non-zero, try and use multi-threaded encoding. - public int thread_level; + public int thread_level; /// If set, reduce memory usage (but increase CPU use). public int low_memory; /// Near lossless encoding [0 = max loss .. 100 = off (default)]. - public int near_lossless; + public int near_lossless; /// If non-zero, preserve the exact RGB values under transparent area. Otherwise, discard this invisible RGB information for better compression. The default value is 0. public int exact; /// Reserved for future lossless feature @@ -1652,20 +1836,20 @@ public struct WebPConfig /// Main exchange structure (input samples, output bytes, statistics) [StructLayoutAttribute(LayoutKind.Sequential)] - public struct WebPPicture: IDisposable + public struct WebPPicture : IDisposable { /// Main flag for encoder selecting between ARGB or YUV input. Recommended to use ARGB input (*argb, argb_stride) for lossless, and YUV input (*y, *u, *v, etc.) for lossy public int use_argb; /// colorspace: should be YUV420 for now (=Y'CbCr). Value = 0 - public UInt32 colorspace; + public uint colorspace; /// Width of picture (less or equal to WEBP_MAX_DIMENSION) public int width; /// Height of picture (less or equal to WEBP_MAX_DIMENSION) public int height; /// Pointer to luma plane. - public IntPtr y; + public IntPtr y; /// Pointer to chroma U plane. - public IntPtr u; + public IntPtr u; /// Pointer to chroma V plane. public IntPtr v; /// Luma stride. @@ -1675,19 +1859,19 @@ public struct WebPPicture: IDisposable /// Pointer to the alpha plane public IntPtr a; /// stride of the alpha plane - public int a_stride; + public int a_stride; /// Padding for later use. [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)] private readonly uint[] pad1; /// Pointer to argb (32 bit) plane. - public IntPtr argb; + public IntPtr argb; /// This is stride in pixels units, not bytes. public int argb_stride; /// Padding for later use. [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)] private readonly uint[] pad2; /// Byte-emission hook, to store compressed bytes as they are ready. - public IntPtr writer; + public IntPtr writer; /// Can be used by the writer. public IntPtr custom_ptr; // map for extra information (only for lossy compression mode) @@ -1696,18 +1880,18 @@ public struct WebPPicture: IDisposable /// if not NULL, points to an array of size ((width + 15) / 16) * ((height + 15) / 16) that will be filled with a macroblock map, depending on extra_info_type. public IntPtr extra_info; /// Pointer to side statistics (updated only if not NULL) - public IntPtr stats; + public IntPtr stats; /// Error code for the latest error encountered during encoding - public UInt32 error_code; + public uint error_code; /// If not NULL, report progress during encoding. public IntPtr progress_hook; /// this field is free to be set to any value and used during callbacks (like progress-report e.g.). - public IntPtr user_data; + public IntPtr user_data; /// Padding for later use. [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 13, ArraySubType = UnmanagedType.U4)] private readonly uint[] pad3; /// Row chunk of memory for yuva planes - private readonly IntPtr memory_; + private readonly IntPtr memory_; /// row chunk of memory for argb planes private readonly IntPtr memory_argb_; /// Padding for later use. @@ -1725,9 +1909,9 @@ public void Dispose() public struct WebPAuxStats { /// Final size - public int coded_size; + public int coded_size; /// Peak-signal-to-noise ratio for Y - public float PSNRY; + public float PSNRY; /// Peak-signal-to-noise ratio for U public float PSNRU; /// Peak-signal-to-noise ratio for V @@ -1735,13 +1919,13 @@ public struct WebPAuxStats /// Peak-signal-to-noise ratio for All public float PSNRALL; /// Peak-signal-to-noise ratio for Alpha - public float PSNRAlpha; + public float PSNRAlpha; /// Number of intra4 public int block_count_intra4; /// Number of intra16 public int block_count_intra16; /// Number of skipped macroblocks - public int block_count_skipped; + public int block_count_skipped; /// Approximate number of bytes spent for header public int header_bytes; /// Approximate number of bytes spent for mode-partition #0 @@ -1771,13 +1955,13 @@ public struct WebPAuxStats /// Approximate number of bytes spent for uv coefficients for segment 3. public int residual_bytes_uv_segments3; /// Number of macroblocks in segments 0 - public int segment_size_segments0; + public int segment_size_segments0; /// Number of macroblocks in segments 1 - public int segment_size_segments1; + public int segment_size_segments1; /// Number of macroblocks in segments 2 - public int segment_size_segments2; + public int segment_size_segments2; /// Number of macroblocks in segments 3 - public int segment_size_segments3; + public int segment_size_segments3; /// Quantizer values for segment 0 public int segment_quant_segments0; /// Quantizer values for segment 1 @@ -1797,23 +1981,23 @@ public struct WebPAuxStats /// Size of the transparency data public int alpha_data_size; /// Size of the enhancement layer data - public int layer_data_size; + public int layer_data_size; // lossless encoder statistics /// bit0:predictor bit1:cross-color transform bit2:subtract-green bit3:color indexing - public Int32 lossless_features; + public int lossless_features; /// Number of precision bits of histogram - public int histogram_bits; + public int histogram_bits; /// Precision bits for transform - public int transform_bits; + public int transform_bits; /// Number of bits for color cache lookup - public int cache_bits; + public int cache_bits; /// Number of color in palette, if used - public int palette_size; + public int palette_size; /// Final lossless size - public int lossless_size; + public int lossless_size; /// Lossless header (transform, huffman etc) size - public int lossless_hdr_size; + public int lossless_hdr_size; /// Lossless image data size public int lossless_data_size; /// Padding for later use. @@ -1847,13 +2031,13 @@ public struct WebPDecBuffer /// Output buffer parameters. public RGBA_YUVA_Buffer u; /// padding for later use. - private readonly UInt32 pad1; + private readonly uint pad1; /// padding for later use. - private readonly UInt32 pad2; + private readonly uint pad2; /// padding for later use. - private readonly UInt32 pad3; + private readonly uint pad3; /// padding for later use. - private readonly UInt32 pad4; + private readonly uint pad4; /// Internally allocated memory (only when is_external_memory is 0). Should not be used externally, but accessed via WebPRGBABuffer. public IntPtr private_memory; } @@ -1943,15 +2127,15 @@ public struct WebPDecoderOptions /// alpha dithering strength in [0..100] public int alpha_dithering_strength; /// padding for later use. - private readonly UInt32 pad1; + private readonly uint pad1; /// padding for later use. - private readonly UInt32 pad2; + private readonly uint pad2; /// padding for later use. - private readonly UInt32 pad3; + private readonly uint pad3; /// padding for later use. - private readonly UInt32 pad4; + private readonly uint pad4; /// padding for later use. - private readonly UInt32 pad5; + private readonly uint pad5; }; #endregion } \ No newline at end of file diff --git a/SETUNA/Properties/AssemblyInfo.cs b/SETUNA/Properties/AssemblyInfo.cs index 5bc192a..d392bd6 100644 --- a/SETUNA/Properties/AssemblyInfo.cs +++ b/SETUNA/Properties/AssemblyInfo.cs @@ -13,4 +13,4 @@ [assembly: AssemblyDescription("")] [assembly: AssemblyCompany("clearup")] [assembly: NeutralResourcesLanguage("zh-CN")] -[assembly: AssemblyVersion("3.0.0.5")] +[assembly: AssemblyVersion("3.0.0.6")] diff --git a/SETUNA/SETUNA.csproj b/SETUNA/SETUNA.csproj index cca601c..14841a0 100644 --- a/SETUNA/SETUNA.csproj +++ b/SETUNA/SETUNA.csproj @@ -105,12 +105,23 @@ ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Svg.2.2.1\lib\net35\Svg.dll + + + ..\packages\System.Drawing.PSD.0.1.0\lib\net40\System.Drawing.PSD.dll + + + ..\packages\TgaLib.1.0.2\lib\net461\TgaLib.dll + + diff --git a/SETUNA/packages.config b/SETUNA/packages.config index 209b951..1d248c6 100644 --- a/SETUNA/packages.config +++ b/SETUNA/packages.config @@ -3,4 +3,7 @@ + + + \ No newline at end of file