Microsoft .NET has been making quite a bit of headway in the developer community recently with both the open source efforts and the upcoming ASP.NET 5 modern web framework. With so much attention on making .NET a “hip” platform (and hopefully breaking into the startup ecosystem), I would like to draw attention to a very frustrating problem that I hope the .NET team can address, .NET produces pretty poor quality JPEGs.
Now if this were some obscure .NET function that hardly anyone uses I wouldn’t care as much, but JPEGs are an integral part of most desktop, mobile softwares and web services today. Granted PNGs and SVGs have become common for UI graphics, but JPEG is still the leading compressed image format used in uploaded avatars and photos (I look forward to the day WebP and BGP is the norm).
I like paying attention to detail, so it horrifies me just how much detail is lost through the Microsoft .NET JPEG encoder. There’s no better way to explain this than to show examples.
I first ran into this problem a few months ago when I was working with images in a podcast RSS feed. I was simply grabbing an online image, resizing it and then saving a JPEG file for local caching. A simple image optimization workflow that almost every modern app will do.
This was the image I was working with. (PNG lossless, 147KB)
And this is what happens after I save it as a JPEG in .NET. (JPEG 100% quality, 70.2KB)
To the untrained eye, they might look not too different, but I invite you to look closer especially around the letters. Here’s the two side by side zoomed in.
Now I know what you’re thinking. Surely that’s just JPEG compression right? Well Photoshop’s JPEG encoder handles it fine. (JPEG 100% quality, 83.7KB)
The thing about JPEG encoding is that it is all proprietary. You can encode JPEG many different ways and it will produce different files or varying fidelity and compression size. Yes they all have a “quality” parameter, but the same “quality” across two apps won’t produce the same result. (In my own testing, the Adobe Photoshop JPEG encoder seems to be the state-of-the-art.)
To cut a long story short, my workaround was to save the file as a PNG (which .NET is quite capable of) and then use the ImageMagick Windows command-line executable to convert it to a JPEG file. The result is worth the hassle. (JPEG 90%, 38.5KB).
Here’s the before and after comparison between .NET’s JPEG and ImageMagick’s JPEG with a 55% size saving (38.5KB vs. 70.2KB). I don’t think I need to tell you which one is which.
My advice to any .NET developer is to avoid the built-in JPEG encoder – the quality is worse and the file size is larger than what it should be. Combined with the fact that almost all .NET best practices and image processing libraries (including the cool imageresizing.net) relies on the .NET JPEG encoder, I imagine this to be a pretty widespread issue.
I’m actually not the first person to notice this. A StackOverflow answer and blog post (images no longer work) by Chris Moschini also refers to this issue.
Perhaps with a bit of encouragement the Microsoft .NET team can fix this for .NET 2015 (it is not fixed in .NET Framework 4.6 Preview). Won’t somebody please think of the pixels?