Back to RyanCooper.com - Resources


Saving Images to WebP in .NET

If you have ever ruin the lighthouse utility offered in chrome to assess your site for performance on mobile devices, you may have seen a recommendation that you present images in "Next Gen formats" like WebP or JPEG2000. After considerable searching - I did not find any easy way to do this in the .NET framework. You can use a proprietary licensed component like ASPOSE, but these come with steep license cost and limitations on use. And frankly you don't need all that, unless you do.

So, how do you save Images in these magical "Next Gen" formats? First - you should choose a format you are going to try to use. I went with WebP because it seems to have fairly wide support in browser- albeit using less than desirable code patterns to make it work. More on that later. But assuming you also feel that WebP is a good option - the next challenge is how do I actually integrate this into a production environment? Assuming you build dynamic web sites (why else would you be reading this?) - I am going to start from the assumption that you already have a tool for users to upload an image to their web site. That's pretty simple stuff that is natively supported in VB.net/C#. And as you will see later - just uploading a webP file is not going to be enough anyway - you really need BOTH webP and JPG/PNG for an acceptable degredation on older browsers without next gen image support. ASP.NET GDI+ simply does not offer webP format when saving. So how are you going to allow a user to upload a widely available format like JPG or PNG, and create a webP copy?

The solution I came up with was to go straight to the source. The WebP format was developed by none other than Google (Who also just happen to make the lighthouse utility... coincidence? I think not.) But in any case - as self serving as it may be that their performance analysis tool recommends their proprietary image format - at least you can't argue with the results. It does a great job of compressing images by 30%+ off the same JPG/PNG file size, with no discernable loss in fidelity (at least not to mine eyes!).

So heres how you can implement a simple, inline process to convert your cumbersome JPEGS into a sexy "Next Gen" format, and in doing so, bump those lighthouse scores up by a substantial amount. First - what you need to do is get the executable for the OS you are using. The WebP exe files can be found here (As of May 2021):
https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html
Once you have downloaded your EXE, extract the files and find the cwebp.exe file in the bin. This is the file that converts any file TO webP from another format. Once you have done this, put the cwebp.exe into a folder on your web server outside your web project. (Do not put it inside your web project - for security NEVER put EXE's inside your web folders).

So now that you have set the table, how do you call this exe inline to your code. Well, thats actually not too difficult - just use the System.Diagnostics class. This class is a conveient way to extend functionality of your site to use any command line utility you need to call, with arguments pased in. For simplicity, I recommend you wrap this conversion code into a function call. The function below will take your origin file, destination file, and a quality setting (80 is pretty good). This will cause your application to call the cwebp.exe process and resave a file inline with your code. In the example below - the cwebP is in a folder on drive X: - This should be changed to the actual location where you place the cwebp.exe file.

Function SaveAsWebP(origin, destination, quality)
    Dim strReturn = "RETURNED NULL"
    Dim args = "cwebp " & origin & " -q " & quality & " -o " & destination & " "
    Dim startinfo = New ProcessStartInfo()
    startinfo.FileName = "X:\libwebp-1.2.0-windows-x64\libwebp-1.2.0-windows-x64\bin\cwebp.exe"
    startinfo.Arguments = args
    startinfo.RedirectStandardOutput = True
    startinfo.CreateNoWindow = False
    startinfo.UseShellExecute = False

    Using proc As Process = New Process()
        proc.StartInfo = startinfo
        proc.Start()
        strReturn = proc.StandardOutput.ReadToEnd()
    End Using

    Return strReturn
End Function

        


You shoud now have your webP code ready to call. To call the function, just use a simple call like this, passing in your source and destination file paths. Make sure you save images to a directory your web site can see - which usually will be a virtual directory (again for security, you should keep this image folder outside your actual web application directory.):

        Dim strOriginFile as String =  "X:\images\portrait.jpg"
        Dim strDestinationFile as String = "X:\images\portrait.webp"
        Dim intQuality as Integer = 80
        
        SaveAsWebP(strOriginFile, strDestinationFile, intQuality)
        
And presto! You now have the two files in your /images/ directory, one JPG and one WEBP.

But before you get too excited - we are not done yet. You see- simply having a webp file does not do much good - you need to be able to display the file on your web site. So, I know you are probably thinking OK, place the new path in my img src and call it a day right? Not quite. First - there is some setup you will need to do. On your IIS server (or whatever platform you use) you will probably need to let the server know that webP is a valid MIME type. So to do so, go into the IIS Snap In, click the server node, and chose the MIME types feature.

Add a Mime type: image/webp

This will instruct the server to actually serve up this file to a browser instead of treating like an unknown mystery format (Which will yield a broken image link or other undesirable behavior). OK, so now, img src right? Nope. This is where it gets a bit tricky. What you need now is the newer generation HTML "picture" tag. So like I said before - the code pattern is not real pretty. Instead of a nice simple, clean HTML img tag - now each image on our site that will use this webp format must have basically a cascading set of instructions to the browser - telling it what formats are avaialable for this picture, and presenting several "options" to the browser depending on the browsers capabilities. So, as an example - chrome will use the webp, but IE 11, being the fiercely independent and cantankerous browser that it is, will simply not show this format. Instead IE 11 will use the jpg version. Lets take a look at how this tag might look:
<picture>
        <source srcset='/images/portrait.webp' type='image/webp'>
        <source srcset='/images/portrait.jpg' type='image/jpeg'>
        <img src='/images/portrait.jpg' alt='Portrait of Mona Lisa' border='0' class='myPicture'>
</picture>
        
So you can see that the once simple and elegant img tag has now been extended to a more complex object structure with multiple sources tied to specific mime type capabilties in the browser. This should get you where you needed to go - a webp file for modern browsers, and an img tag referring to the orginal JPG for old dogs. Thos old dogs will just ignore the newfangled picture tag and serve the basic jpg. Also note - for accessibility - don't forget your alt tag on every img!