From eebb79819a9ccf151c5e96391b8ed4bdcc0faf5c Mon Sep 17 00:00:00 2001 From: James South Date: Tue, 24 Jun 2014 23:32:06 +0100 Subject: [PATCH] Adding transparent webp support Former-commit-id: bde73846512628e8364d54420c2f1dac7af748e4 --- .../HttpModules/ImageProcessingModule.cs | 5 +- .../Imaging/Formats/WebPFormat.cs | 71 +++++++++++++------ src/ImageProcessorConsole/Program.cs | 27 +++++-- .../images/input/circle.png | 3 + .../images/input/rotate.jpg.REMOVED.git-id | 1 + .../images/output/circle.webp | 3 + .../images/output/rotate.webp | 3 + 7 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 src/ImageProcessorConsole/images/input/circle.png create mode 100644 src/ImageProcessorConsole/images/input/rotate.jpg.REMOVED.git-id create mode 100644 src/ImageProcessorConsole/images/output/circle.webp create mode 100644 src/ImageProcessorConsole/images/output/rotate.webp diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs index 91ec40aab..f2bd71ad3 100644 --- a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs +++ b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs @@ -359,13 +359,14 @@ namespace ImageProcessor.Web.HttpModules IPrincipal user = context.User ?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]); // Do we have permission to call UrlAuthorizationModule.CheckUrlAccessForPrincipal? - PermissionSet permission = new PermissionSet(PermissionState.None); + PermissionSet permission = new PermissionSet(PermissionState.Unrestricted); permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted)); bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet); bool isAllowed = true; - // Run the rewritten path past the auth system again, using the result as the default "AllowAccess" value + // Run the rewritten path past the authorization system again. + // We can then use the result as the default "AllowAccess" value if (hasPermission && !context.SkipAuthorization) { isAllowed = UrlAuthorizationModule.CheckUrlAccessForPrincipal(virtualCachedPath, user, "GET"); diff --git a/src/ImageProcessor/Imaging/Formats/WebPFormat.cs b/src/ImageProcessor/Imaging/Formats/WebPFormat.cs index 5253983e3..15e4e4313 100644 --- a/src/ImageProcessor/Imaging/Formats/WebPFormat.cs +++ b/src/ImageProcessor/Imaging/Formats/WebPFormat.cs @@ -13,6 +13,7 @@ namespace ImageProcessor.Imaging.Formats { using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Imaging; @@ -20,6 +21,8 @@ namespace ImageProcessor.Imaging.Formats using System.Runtime.InteropServices; using System.Text; + using ImageProcessor.Core.Common.Exceptions; + /// /// Provides the necessary information to support webp images. /// Adapted from @@ -72,6 +75,35 @@ namespace ImageProcessor.Imaging.Formats } } + /// + /// Applies the given processor the current image. + /// + /// The processor delegate. + /// The . + public override void ApplyProcessor(Func processor, ImageFactory factory) + { + base.ApplyProcessor(processor, factory); + + // Set the property item information from any Exif metadata. + // We do this here so that they can be changed between processor methods. + if (factory.PreserveExifData) + { + foreach (KeyValuePair propertItem in factory.ExifPropertyItems) + { + try + { + factory.Image.SetPropertyItem(propertItem.Value); + } + // ReSharper disable once EmptyGeneralCatchClause + catch + { + // Do nothing. The image format does not handle EXIF data. + // TODO: empty catch is fierce code smell. + } + } + } + } + /// /// Decodes the image to process. /// @@ -151,49 +183,48 @@ namespace ImageProcessor.Imaging.Formats IntPtr ptrData = pinnedWebP.AddrOfPinnedObject(); uint dataSize = (uint)webpData.Length; - int imgWidth; - int imgHeight; + int width; + int height; - if (WebPGetInfo(ptrData, dataSize, out imgWidth, out imgHeight) != 1) + if (WebPGetInfo(ptrData, dataSize, out width, out height) != 1) { - // TODO: Throw error? - return null; + throw new ImageFormatException("WebP image is corrupted."); } // Create a BitmapData and Lock all pixels to be written - Bitmap bmp = new Bitmap(imgWidth, imgHeight, PixelFormat.Format24bppRgb); - BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); + Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); + BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); // Allocate memory for uncompress image - int outputBufferSize = bmpData.Stride * imgHeight; + int outputBufferSize = bitmapData.Stride * height; IntPtr outputBuffer = Marshal.AllocHGlobal(outputBufferSize); // Uncompress the image - outputBuffer = WebPDecodeBGRInto(ptrData, dataSize, outputBuffer, outputBufferSize, bmpData.Stride); + outputBuffer = WebPDecodeBGRAInto(ptrData, dataSize, outputBuffer, outputBufferSize, bitmapData.Stride); // Write image to bitmap using Marshal byte[] buffer = new byte[outputBufferSize]; Marshal.Copy(outputBuffer, buffer, 0, outputBufferSize); - Marshal.Copy(buffer, 0, bmpData.Scan0, outputBufferSize); + Marshal.Copy(buffer, 0, bitmapData.Scan0, outputBufferSize); // Unlock the pixels - bmp.UnlockBits(bmpData); + bitmap.UnlockBits(bitmapData); // Free memory pinnedWebP.Free(); Marshal.FreeHGlobal(outputBuffer); - return bmp; + return bitmap; } /// - /// Lossly encodes the image in bitmap. + /// Lossy encodes the image in bitmap. /// /// /// Bitmap with the image /// /// - /// Quality. 0 = minimum ... 100 = maximimun quality + /// Quality. 0 = minimum ... 100 = maximum quality /// /// /// The byte array containing the encoded image data. @@ -207,9 +238,9 @@ namespace ImageProcessor.Imaging.Formats try { - BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); IntPtr unmanagedData; - int size = WebPEncodeBGR(bmpData.Scan0, bitmap.Width, bitmap.Height, bmpData.Stride, quality, out unmanagedData); + int size = WebPEncodeBGRA(bmpData.Scan0, bitmap.Width, bitmap.Height, bmpData.Stride, quality, out unmanagedData); // Copy image compress data to output array webpData = new byte[size]; @@ -266,16 +297,16 @@ namespace ImageProcessor.Imaging.Formats /// Size of allocated buffer /// /// - /// Specifies the distance between scanlines + /// Specifies the distance between scan-lines /// /// /// output_buffer if function succeeds; NULL otherwise /// [DllImport("libwebp.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr WebPDecodeBGRInto(IntPtr data, uint dataSize, IntPtr outputBuffer, int outputBufferSize, int outputStride); + private static extern IntPtr WebPDecodeBGRAInto(IntPtr data, uint dataSize, IntPtr outputBuffer, int outputBufferSize, int outputStride); /// - /// Lossless encoding images pointed to by *data in WebP format + /// Lossy encoding images pointed to by *data in WebP format /// /// /// Pointer to RGB image data @@ -299,7 +330,7 @@ namespace ImageProcessor.Imaging.Formats /// Size of WebP Image /// [DllImport("libwebp.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern int WebPEncodeBGR(IntPtr rgb, int width, int height, int stride, float qualityFactor, out IntPtr output); + private static extern int WebPEncodeBGRA(IntPtr rgb, int width, int height, int stride, float qualityFactor, out IntPtr output); /// /// Frees the unmanaged memory. diff --git a/src/ImageProcessorConsole/Program.cs b/src/ImageProcessorConsole/Program.cs index cb33aef79..20e92e803 100644 --- a/src/ImageProcessorConsole/Program.cs +++ b/src/ImageProcessorConsole/Program.cs @@ -10,8 +10,11 @@ namespace ImageProcessorConsole { using System; + using System.Collections.Generic; using System.Drawing; using System.IO; + using System.Linq; + using ImageProcessor; using ImageProcessor.Imaging.Formats; @@ -37,8 +40,10 @@ namespace ImageProcessorConsole di.Create(); } - //FileInfo[] files = di.GetFiles("*.gif"); - FileInfo[] files = di.GetFiles(); + FileInfo[] files = di.GetFiles("*.jpg"); + //FileInfo[] files = di.GetFiles(); + //var files = GetFilesByExtensions(di, ".gif", ".webp"); + foreach (FileInfo fileInfo in files) { @@ -47,20 +52,30 @@ namespace ImageProcessorConsole // ImageProcessor using (MemoryStream inStream = new MemoryStream(photoBytes)) { - using (ImageFactory imageFactory = new ImageFactory()) + using (ImageFactory imageFactory = new ImageFactory(true)) { Size size = new Size(200, 200); // Load, resize, set the format and quality and save an image. imageFactory.Load(inStream) + .AutoRotate() .Constrain(size) - //.Format(new JpegFormat()) + .Format(new WebPFormat()) + .Quality(5) // ReSharper disable once AssignNullToNotNullAttribute - // .Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", Path.GetFileNameWithoutExtension(fileInfo.Name) + ".jpg"))); - .Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name))); + .Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", Path.GetFileNameWithoutExtension(fileInfo.Name) + ".webp"))); + //.Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name))); } } } } + + public static IEnumerable GetFilesByExtensions(DirectoryInfo dir, params string[] extensions) + { + if (extensions == null) + throw new ArgumentNullException("extensions"); + IEnumerable files = dir.EnumerateFiles(); + return files.Where(f => extensions.Contains(f.Extension, StringComparer.OrdinalIgnoreCase)); + } } } diff --git a/src/ImageProcessorConsole/images/input/circle.png b/src/ImageProcessorConsole/images/input/circle.png new file mode 100644 index 000000000..3d96cf303 --- /dev/null +++ b/src/ImageProcessorConsole/images/input/circle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3df296a3fd58930899d308558b128db760879cb776afba8f2d6511aba3934dd0 +size 6957 diff --git a/src/ImageProcessorConsole/images/input/rotate.jpg.REMOVED.git-id b/src/ImageProcessorConsole/images/input/rotate.jpg.REMOVED.git-id new file mode 100644 index 000000000..bf0538b24 --- /dev/null +++ b/src/ImageProcessorConsole/images/input/rotate.jpg.REMOVED.git-id @@ -0,0 +1 @@ +406a6a7916628c0c0bea8243565a7162ebd5a505 \ No newline at end of file diff --git a/src/ImageProcessorConsole/images/output/circle.webp b/src/ImageProcessorConsole/images/output/circle.webp new file mode 100644 index 000000000..0a022dff6 --- /dev/null +++ b/src/ImageProcessorConsole/images/output/circle.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac5bfc52fe066984db96d0b169a7fb7ebd76f3a1cf946ba02a167475377903c2 +size 1228 diff --git a/src/ImageProcessorConsole/images/output/rotate.webp b/src/ImageProcessorConsole/images/output/rotate.webp new file mode 100644 index 000000000..fa870fffe --- /dev/null +++ b/src/ImageProcessorConsole/images/output/rotate.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3543de5854cc0a8a95b5249548abc8150040542a127ac6d89eb6338b8dfa3a83 +size 1812