It all started with a simple barcode. I was refactoring my old Code128 barcode class to work in .NET 5 and faced an interesting issue - there was no option to output barcode as an image. Yes, on Windows you could rely on
System.Drawing but .NET is supposedly multiplatform environment these days. And no,
System.Drawing is not supported in Linux - you only get
error CS0234: The type or namespace name 'Drawing' does not exist in the namespace 'System' (are you missing an assembly reference?.
If you're thinking to yourself "Wait, I remember Microsoft's
System.Drawing.Common working on Linux", you don't have a bad memory. However, Microsoft since made a small update and, with a stroke of a pen, decided to abandon it. Yes, you can use runtime options as a workaround but official support is no longer there.
Alternatives do exist. Most notably both ImageSharp and SkiaSharp would satisfy any graphic need I might have. But both also present quite a big dependency to pull for what is essentially a trivial task. I mean, how difficult can it be to implement PNG writer?
It turns out, not difficult at all. If you go over specification you'll notice there are only three chunks you need to support:
IEND. If you know how to write those three, you can output PNG image that's readable by all. Literally the only two things that are not straightforward were deflate compression that required an extra header and dealing with CRC.
Most of my debugging time was actually spend dealing with output not showing properly in IrfanView. I could read my output image in Paint.NET, Paint, Gimp, and multitude of other programs I've tried. It took me a while before I figured out that IrfanView
4.54 is actually one with the issue. Update to the latest version sorted that one out.
In any case, I successfully added PNG support to by barcode class.
And then I started thinking... Why not make a simple PNG image reader/writer? Well, answers are numerous. First of all, alternatives exist and a lone developer can never hope to have such level of completeness. Secondly, I don't deal with graphics on a daily basis and thus features would be few and far between. And lastly, while PNG is a relatively simple specification to start with, it has a lot of optional complexity. And some of that complexity would require me to learn much more than I ever want to know (yes, ICC profiles - I'm looking at you).
However, logic be damned. What's the better use of an afternoon than writing a PNG parser?
In any case, the final product is here and while as simple as it gets it's relatively feature complete when it comes to loading plain old PNG images. The only mandatory feature I failed to implement is support for interlaced images.
Expectedly, the end result is usable for changing a pixel or two but not for much more as all infrastructure is missing. I will add some of the missing functionality in the future, resize and interlacing support coming first, but I consider this class reasonably feature complete for what I both need and can do without making a full blown library. And I haven't had as much fun programming in a while.