Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • aerksn 7 posts 78 karma points
    Jun 17, 2022 @ 09:10
    aerksn
    0

    Umbraco 10 WebP file much larger than png

    Just updated to version 10 and tried the webp encoding Isn't webp suppose to be smaller, or not atleast 8-10x larger ?

    enter image description here

    https://appnitecloud.azurewebsites.net/media/c1cn1kpg/gympartnervit.png?format=webp

    webp = 125KB

    https://appnitecloud.azurewebsites.net/media/c1cn1kpg/gympartnervit.png?format=png

    png = 19KB

  • Dennis 75 posts 397 karma points MVP
    Jun 17, 2022 @ 13:42
    Dennis
    0

    Hi aerksn,

    On first thought, yeah, usually images encoded as webp are supposed to be smaller. Looking at your image though, I see it has very few different colours and if I remember correctly, png is already quite optimised if there are few different colours. I'd say: try the same thing with a photograph of the same dimensions or try it with a jpeg version of the same image and see if webp is still larger than the original image.

    kind regards, Dennis

  • aerksn 7 posts 78 karma points
    Jun 17, 2022 @ 20:26
    aerksn
    0

    jpeg and all other formats works fine, except all pngs. Don't know if i ever had issue with that before.

    Here the exact same image on .framework (umbraco 8) https://www.gympartner.nu/media/nw3dmqcc/gympartnervit.png?format=webp

    I did a workaround for now on it, (thanks @Kamil for the tip)

    services.AddImageSharp(options =>
            {
                options.OnParseCommandsAsync = c =>
                {
                    bool wantsformat = c.Commands.TryGetValue("format", out string value);                   
    
                    if (wantsformat)
                    {
                        bool isPng = c.Context.Request.Path.Value.Contains(".png");
    
                        if (isPng && value == "webp")
                            c.Commands.Remove("format");
                    }
                    return Task.CompletedTask;
                };
            });
    
  • Kamil 6 posts 79 karma points
    Jun 18, 2022 @ 05:56
    Kamil
    2

    aerksn,

    I think in general you're losing performance with this as I think your logo is a bit of a special case. The WebP optimization for other png images should still be (very) helpful.

    In your code you're removing format - so you're "reverting" developer intent (I assume you're appending the ?format= to every image src?)

    How about doing this in another way. You can implement for example a middleware:

    public class WebPMiddleware
    {
        private readonly RequestDelegate _next;
    
        public WebPMiddleware(RequestDelegate next)
        {
            _next = next;
    
        }
    
        public async Task Invoke(HttpContext httpContext)
        {
            if (IsImageRequest(httpContext) &&
                httpContext.Request.GetTypedHeaders()?.Accept != null 
                && httpContext.Request.GetTypedHeaders().Accept.Any(aValue => aValue.MediaType.Value == "image/webp"))
            {
                var updatedQueryString = GetUpdatedQueryString(httpContext);
    
                var qb1 = new QueryBuilder(updatedQueryString);
    
                httpContext.Request.QueryString = qb1.ToQueryString();
            }
    
            await _next(httpContext);
        }
    
        private bool IsImageRequest(HttpContext httpContext)
        {
            var path = httpContext.Request.Path.ToString();
    
            return path.EndsWith("png") || path.EndsWith("jpg") || path.EndsWith("jpeg");
        }
    
        private List<KeyValuePair<string, string>> GetUpdatedQueryString(HttpContext httpContext)
        {
            var queryitems = httpContext.Request.Query.SelectMany(x => x.Value, (col, value) => new KeyValuePair<string, string>(col.Key, value)).ToList();
            var queryParameters = new List<KeyValuePair<string, string>>();
    
            foreach (var item in queryitems)
            {
                var value = item.Value;
    
                if (item.Key == "format")
                {
                    value = "webp";
                }
    
                var newQueryParameter = new KeyValuePair<string, string>(item.Key, value);
    
                queryParameters.Add(newQueryParameter);
            }
    
            if (!queryParameters.Any())
            {
                queryParameters.Add(new KeyValuePair<string, string>("format", "webp"));
            }
    
            return queryParameters;
        }
    }
    public static class WebPMiddlewareExtensions
    {
        public static IApplicationBuilder UseWebP(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<WebPMiddleware>();
        }
    }
    

    use it:

            app.UseWebP();
    
            app.UseImageSharp();
    

    and then transform your code to something else:

         services.AddImageSharp(options =>
        {
            options.OnParseCommandsAsync = c =>
            {
                bool doesntWantFormat = c.Commands.TryGetValue("noformat", out string value);                   
    
                if (doesntWantFormat)
                {
                        c.Commands.Remove("format");
                }
                return Task.CompletedTask;
            };
        });
    

    and then WebP works by default for all images except the ones you want to have it disabled, like this:

    https://appnitecloud.azurewebsites.net/media/c1cn1kpg/gympartnervit.png?noformat=true
    

    I tested the above code and it works - just a random saturday idea :)

  • Matthew 13 posts 88 karma points
    Jun 18, 2022 @ 15:36
    Matthew
    0

    Hey, this solution works great. Thanks for sharing. One thing I've found however with both solutions is that neither works when you enable CDN URL with the Azure Blob Storage. Any ideas on how to get the WEBP functionality working when utilizing the CDN URL as well?

  • Kamil 6 posts 79 karma points
    Jun 18, 2022 @ 15:47
    Kamil
    0

    I'm glad you like it!

    I'm not utilising CDN in my project so unfortunately I can't play with it without doing some more setting up for which I don't have time today but I have an idea.

    The first thing that came into my mind is that you might need to implement custom image provider - more in the links:

    https://our.umbraco.com/forum/using-umbraco-and-getting-started/108123-umbraco9-local-and-remote-images

    https://github.com/SixLabors/ImageSharp.Web/discussions/167

    Let us know how you get on!

  • aerksn 7 posts 78 karma points
    Jun 20, 2022 @ 21:18
    aerksn
    1

    Thanks for the reply and thanks to all of you for your tips. I've tried and tried different approaches to get to a good solution that works for me.

    After some digging in the encoder i found "Image.PixelType.BitsPerPixel" and found a correlation between png and jpegs.

    The jpges are 24px and pngs are 32px, in that way i could sepparate them. And depending on that use different compression types. Which made it all work. Haha, 🙌

    So now i'm getting the images in correct size.

    Idon't auctually know what i'm doing but it works... haha

         services.AddImageSharp(options =>
            {
                options.OnBeforeSaveAsync = c =>
                {
                    if(c.Encoder.GetType().Name == "WebpEncoder"){
                        c.Encoder = new WebpEncoder()
                        {
                            FileFormat = c.Image.PixelType.BitsPerPixel < 30 ? WebpFileFormatType.Lossy : WebpFileFormatType.Lossless,                          
                        };
                    }                 
                    return Task.CompletedTask;
                };
            });
    

    (btw you can edit all properties in the encoder object as well if you want to tweak it even more)

  • Arjan H. 221 posts 457 karma points c-trib
    Jul 19, 2022 @ 15:37
    Arjan H.
    0

    I believe the 32 bits per pixel has to do with transparency: an additional 8 bits for the alpha channel. So I guess it's a good way to determine if a PNG contains transparency. However, I'm not sure if using Lossless WebP encoding for 32 bpp images will always result in a smaller image than the original PNG. It may depend on the image, see the following discussion:

    https://github.com/SixLabors/ImageSharp/discussions/1957

    Have you experienced any issues with your code? Or did all the JPG and PNG images that you run through this code have a smaller WebP encoded image?

  • Matthew 13 posts 88 karma points
    Jun 17, 2022 @ 14:32
    Matthew
    0

    Hey there, just curious, how do you enable webp encoding for images in v10? Just adding the ?format=webp still shows encoding as the original format in the network tab for me.

  • Kamil 6 posts 79 karma points
    Jun 17, 2022 @ 15:23
    Kamil
    102

    Matthew, I came across same problem despite hearing that it should happen OOTB.

    I've enabled the ImageSharp2 manually, in Startup.cs: in ConfigureServices:

            services.AddImageSharp();
    

    in Configure:

            app.UseImageSharp();
    

    PS If these methods aren't found there's a nuget SixLabors.ImageSharp in v2.1.2

    PS2 Remember to add the above line before UseStaticFiles!

  • Matthew 13 posts 88 karma points
    Jun 17, 2022 @ 15:58
    Matthew
    2

    Thank you for the tip, it looks like that worked!

  • Aaron 57 posts 405 karma points MVP c-trib
    Jun 22, 2022 @ 16:17
    Aaron
    0

    Hey Kamil!

    I've tried your code in V10, however it doesn't seem to work.

    Nothing inside of /Media hits your middleware, I presume this is due to the way Umbraco is now registering the static assets in program.cs rather than startup.cs?

  • Andreas Nylin 4 posts 74 karma points
    Dec 20, 2022 @ 09:07
    Andreas Nylin
    0

    I seem to have the same problem in my Umbraco 10 project. Some png images works just fine to convert to webp but some become huge when converted. I have one png image that is about 50kb but when i add format=webp it becomes 1.1 mb. I'm guessing that Image Sharp uses lossless format for this image for some reason. I have tried some of the solutions suggested here but no luck. I have disabled UseStaticFiles but that din't give any effect. I would like to configure Image Sharp to always use lossy compression but I can't seem to figure out how to do that. I tried aerksn solution but it doesn't work for me. The code never runs in my solution.

    Edit: aerksns solution does work in my project. I just forgot that I needed to delete the image cache files on disk. In my case it seems that there is something wrong with two of my png images. I tried to save them as a new png image and used that image instead and they compress just fine as webp.

  • Arjan H. 221 posts 457 karma points c-trib
    Dec 21, 2022 @ 12:59
    Arjan H.
    0

    Could you share the problematic PNG image? I'd like to do some testing.

  • Luuk Peters 79 posts 319 karma points
    Dec 21, 2022 @ 12:54
    Luuk Peters
    0

    So far, .NET is really lousy in generating PNG files. In Umbraco 8 this was also a problem. When creating a new crop from a png, the resulting PNG (although smaller in number of pixels) would be way larger in file size. My assumption is that PNG's are always rendered in 32bit mode, even though it's not needed.

    In my opinion, PNG's should be used as little as possible. For images, use JPG and WEBP and for graphics and icons, use svg. Only use PNG if you need the transparency.

  • Arjan H. 221 posts 457 karma points c-trib
    Dec 21, 2022 @ 12:58
    Arjan H.
    0

    WEBP also offers transparency, so you could avoid using PNG altogether.

  • Chris Hall 15 posts 86 karma points
    Jan 20, 2023 @ 17:20
    Chris Hall
    0

    I am also experiencing this issue, trying with multiple transparent PNG's - all tests have resulted in around 8-10x larger webp, even if forced lossy.

    If I convert to webp via cloudconvert, they come back 10x smaller.

    I downloaded a publicly available example PNG with transparency. available here

    The original is 50kb. After running through ImageSharp, the resulting file is 958kb!! almost 20x larger.

    The same file run through cloudconvert returns a 28k

    Has anyone seen this work as expected with transparent PNGs?

  • Arjan H. 221 posts 457 karma points c-trib
    Jan 20, 2023 @ 19:53
    Arjan H.
    0

    The hack mentioned earlier gave me a good result with that sample file:

    services.AddImageSharp(options =>
            {
                options.OnBeforeSaveAsync = c =>
                {
                    if(c.Encoder.GetType().Name == "WebpEncoder"){
                        c.Encoder = new WebpEncoder()
                        {
                            FileFormat = c.Image.PixelType.BitsPerPixel < 30 ? WebpFileFormatType.Lossy : WebpFileFormatType.Lossless,                          
                        };
                    }                 
                    return Task.CompletedTask;
                };
            });
    

    content-length: 14698

  • Chris Hall 15 posts 86 karma points
    Jan 20, 2023 @ 20:17
    Chris Hall
    0

    Thanks Arjan,

    I was forcing to Lossy, not to Lossless and not testing the bit depth - i just assumed Lossy would always be smaller than Lossless, only clicked when I was testing after your reply! :)

  • Jonas Widenberg 21 posts 102 karma points
    Apr 21, 2023 @ 13:41
    Jonas Widenberg
    0

    Hi. I've kind of successfully implemented the webp configurations described in this post (and summarized in https://umbhost.net/blog/2022/06/dynamic-webp-images-in-umbraco-v10), but I still have a hard time getting my transparent pngs in a reasonable size.

    Served as pngs they are around 12kb but as webp they get about 200kb.

    Has anyone found a solution for this?

  • Arjan H. 221 posts 457 karma points c-trib
    Apr 21, 2023 @ 13:56
    Arjan H.
    0

    Did you try this solution? This code still needs some tweaking though, because I noticed it also caused excessive compression for JPEG images, resulting in low quality WEBP images.

  • Jonas Widenberg 21 posts 102 karma points
    Apr 21, 2023 @ 14:04
    Jonas Widenberg
    0

    Hi. Yeah I added the fix but didn't notice any difference at all.

    services.AddImageSharp(options =>
            {
                options.OnParseCommandsAsync = c =>
                {
                    if (c.Context != null)
                    {
                        if (c.Context.Request.GetTypedHeaders().Accept
                            .Any(aValue => aValue.MediaType.Value == "image/webp"))
                        {
                            var path = c.Context.Request.Path.ToString();
    
                            if (!c.Commands.Contains("webp") ||
                                !c.Commands.Contains("noformat") && path.EndsWith("png") || path.EndsWith("jpg") ||
                                path.EndsWith("jpeg"))
                            {
                                c.Commands.Remove("format");
                                c.Commands.Add("format", "webp");
                                c.Context.Response.Headers.Add("Vary", "Accept");
                            }
                        }
                    }
    
                    bool doesntWantFormat = c.Commands.TryGetValue("noformat", out string value);
    
                    if (doesntWantFormat)
                    {
                        c.Commands.Remove("format");
                    }
    
                    return Task.CompletedTask;
                };
                options.OnBeforeSaveAsync = c =>
                {
                    if (c.Encoder.GetType().Name == "WebpEncoder")
                    {
                        c.Encoder = new WebpEncoder
                        {
                            FileFormat = c.Image.PixelType.BitsPerPixel < 30 ? WebpFileFormatType.Lossy : WebpFileFormatType.Lossless,
                        };
                    }
                    return Task.CompletedTask;
                };
            });
    
  • Arjan H. 221 posts 457 karma points c-trib
    Apr 21, 2023 @ 14:06
    Arjan H.
    0

    Could you share one of the PNG's that becomes very big when converted to WEBP?

  • Jonas Widenberg 21 posts 102 karma points
    Apr 21, 2023 @ 14:55
    Jonas Widenberg
    0

    Here's one. I hope Pasteboard hasn't done anything with it while uploading. I dont think so, because it has the same bytesize.

    https://pasteboard.co/S2cWsEbhSJAg.png

  • Matthew 13 posts 88 karma points
    Apr 21, 2023 @ 14:58
    Matthew
    0

    I am pretty sure we just need Umbraco to update Imagesharp to v3. This issue is listed in the things v3 has fixed. https://sixlabors.com/posts/announcing-imagesharp-300/

  • Arjan H. 221 posts 457 karma points c-trib
    Apr 21, 2023 @ 16:52
    Arjan H.
    0

    The following code used on a fresh install of Umbraco 11.3.0 converted this 19.0 kB PNG image to an 11.5 kB WEBP image. Without the OnBeforeSaveAsync logic that same image is converted to a 128 kB WEBP.

    Don't forget the clear the /umbraco/Data/TEMP/MediaCache folder after changing the code, otherwise you'll just see a previously cached version of the image.

    services.AddImageSharp(options =>
    {
        // Enable Lossless compression for transparent PNG
        options.OnBeforeSaveAsync = c =>
        {
            if (c.Encoder.GetType().Name == "WebpEncoder" && c.Image.PixelType.BitsPerPixel == 32)
            {
                c.Encoder = new WebpEncoder()
                {
                    FileFormat = WebpFileFormatType.Lossless,
                };
            }
            return Task.CompletedTask;
        };
    
        options.OnParseCommandsAsync = c =>
        {
            if (c.Context != null)
            {
                var path = c.Context.Request.Path.ToString();
    
                // Don't convert when the noformat query string is set
                if (c.Context.Request.QueryString.Value?.Contains("noformat") == true)
                    c.Commands.Add("noformat", "1");
    
                // Exclude /umbraco/assets and don't convert if WebP is not supported
                if (path.Contains("/umbraco/assets/") == false &&
                    c.Context.Request.GetTypedHeaders().Accept.Any(x => x.MediaType.Value == "image/webp"))
                {
                    // Only convert PNG and JP(E)G to WebP
                    if (c.Commands.Contains("webp") == false &&
                        c.Commands.Contains("noformat") == false &&
                        (path.EndsWith("png") || path.EndsWith("jpg") || path.EndsWith("jpeg")))
                    {
                        c.Commands.Remove("format");
                        c.Commands.Add("format", "webp");
    
                        if (c.Commands.Contains("quality") == false)
                            c.Commands.Add("quality", "92");
    
                        c.Context.Response.Headers.Add("Vary", "Accept");
                    }
                }
            }
    
            if (c.Commands.Count > 0)
            {
                // Check width and height to provide very basic security
                var width = c.Parser.ParseValue<uint>(
                c.Commands.GetValueOrDefault(ResizeWebProcessor.Width),
                c.Culture);
    
                var height = c.Parser.ParseValue<uint>(
                    c.Commands.GetValueOrDefault(ResizeWebProcessor.Height),
                    c.Culture);
    
                // If width exceeds limit, remove it from request
                if (width > 2400)
                    c.Commands.Remove(ResizeWebProcessor.Width);
    
                // If height exceeds limit, remove it from request
                if (height > 2400)
                    c.Commands.Remove(ResizeWebProcessor.Height);
    
                // Remove format command if noformat command has been set
                if (c.Commands.TryGetValue("noformat", out string value))
                {
                    c.Commands.Remove("format");
                }
            }
    
            return Task.CompletedTask;
        };
    });
    

    And with JPEG's I'm saving roughly 20% in file size when converted to WEBP. Lowering the quality command value will reduce these file sizes even further, eventually with visible loss of image quality. 91 is the minimum quality required to force the encoder to not use 4:2:0 subsampling (source).

    ===

    EDIT: I also tested your PNG. Without the OnBeforeSaveAsync logic it converted to a 261 kB WEBP. When forcing Lossless compression it converted to a 9.2 kB WEBP.

    enter image description here enter image description here enter image description here

  • Jonas Widenberg 21 posts 102 karma points
    Apr 24, 2023 @ 16:20
    Jonas Widenberg
    0

    Thanks Arjan! N00b me hadn't cleared the mediacache folder, so the image got smaller for me to!

    I noticed though that the company logo that is a transparent png gets converted to a completely white image. Any idea what could be going on there?

  • Arjan H. 221 posts 457 karma points c-trib
    Apr 24, 2023 @ 17:00
    Arjan H.
    0

    Which company logo are you referring to? The GymPartner logo? In that case I have no idea why you're getting a completely white image. In my test project the image worked fine with both the Url() and GetCropUrl() methods:

    enter image description here

Please Sign in or register to post replies

Write your reply to:

Draft