Libspng – C library for reading and writing PNG files
libspng.org
libspng.org
But libpng was intended as only a reference implementation—a rigid adherent to the PNG standard, for the sake of having a “runnable version” of the PNG standard—and was never particularly optimized for production use-cases, or code readability/maintainability/low attack surface, or any other criteria you might like to have. Libpng is optimized for one thing only: allowing other PNG implementations to build spec-compatibility test suites by testing against libpng behaviour.
Because of this rigid adherence, libpng doesn’t implement—and will never implement—any features that aren’t in the base PNG spec, like APNG.
And since everyone uses libpng as their PNG implementation (for some reason), nobody ends up adopting these extra features. (Sure, all the major browsers except Edge support APNG, but does your image editor? Does your IoT thermostat whose OS uses PNG image assets? Does your game console? Nah. Because these all just compile in libpng. And this is what kills support for features like APNG in the crib.)
That was never the PNG Working Group’s intent, of course. They don’t want to restrict the development of extra PNG features (yes, even though they developed MNG and might see features like APNG as “competing” with MNG, they don’t really care.) They keep libpng the way it is, not for ideological reasons, but simply because they don’t care about doing anything with libpng that moves it away from “reference implementation of the base PNG spec” territory. (And why would they? Their job as the PNG Working Group is to produce the PNG base spec. It’s supposed to be everyone else’s job, in the ecosystem, to produce conformant implementations. They even gave you libpng to make that easier!)
So, like I said, I’m glad someone is finally writing an alternative implementation (in C), such that the projects that currently use libpng could reasonably choose to switch to libspng. It’s the same feeling I get from seeing LibreSSL and BoringSSL: that an ecosystem that was essentially “dead” and stuck in a set of bad choices due to everyone sticking with one unchanging reference impl, is now coming alive again.
I don’t think that’s accurate. I use LibPNG but I also use other things too.
When I write for Linux or cross platform, my only requirement “decode bytes in RAM to texture in VRAM”, I prefer stb_image for usability.
When I write Windows software, I prefer OS provided codecs. In modern world that’s WIC for C++ code, previously OleLoadPicture API. This way I don’t have to patch image codecs in my software for security bugs, MS does that in windows updates. Also helps with binary size.
Because of this, libpng gets linked in by a lot of the higher-level multiformat libraries (like SDL) or graphics toolkits (like WxWidgets) when their aim is also to be “portable” free-floating multiplatform libraries, rather than libraries that have special per-OS code-paths for everything. (Which becomes especially important in things like embedded OSes/unikernels, or sandboxes like Emscripten, where there aren’t any OS image APIs.) And these portable libraries and toolkits then get used by runtimes that want portability, like Erlang or Love2D; or by software that can’t access the OS’s SDK to link to system libraries, like game-console homebrew.
In these cases, the feature-set of libpng determines the PNG features that these higher-level libraries support. SDL supports loading animated images—but not from APNG, because libpng doesn’t provide SDL with that capability. SDL isn’t going to do the work of adding their own APNG support; they’ve got enough on their plate just making sure all their components stay glued together. But if there was a “better” PNG library than libpng, that ticked all the same portability boxes? They’d switch.
Platform: Windows 10, Visual Studio 2019, RelWithDebInfo config, Intel Core i7-8700K. Compiled with -DSPNG_SSE=3.
testing loading image: D:\art\cryptovoxels\ortho overview.png (32-bit, large)
libSPNG decode took 0.2221 s
libPNG decode took 0.2373 s
libSPNG decode took 0.2215 s
libPNG decode took 0.2364 s
testing loading image: D:\art\dof_test_output.png (32-bit)
libSPNG decode took 0.01101 s
libPNG decode took 0.01225 s
libSPNG decode took 0.01096 s
libPNG decode took 0.01205 s
testing loading image: D:\art\skymodel2_test3\top2 CPU.png (24-bit)
libSPNG decode took 0.008874 s
libPNG decode took 0.009399 s
So Libspng seems slightly faster, but certainly not 35% faster, in these tests. testing loading image: D:\art\cryptovoxels\ortho overview.png
libSPNG decode took 0.1901 s
libPNG decode took 0.2069 s
testing loading image: D:\art\skymodel2_test3\top2 CPU.png
libSPNG decode took 0.007862 s
libPNG decode took 0.007801 sI'd say the performance in general with Libspng and Libpng is pretty similar. The apparent greater speed of Libspng in the RelWithDebInfo build may be due to the use of the inline keyword, which libpng doesn't use. However once full program optimisation is enabled, and functions are inlined across translation units etc.., the code comes out pretty similar and hence has performance parity.
Is it just that it has more features and can handle weird edge cases?
edit: Thank you cormacrelf for the explanation re: lib being mandatory. Thank you OP for explaining your rationale.
I’ve been really distracted lately thinking about what things need named handles in my data models/representations, and what they should be handles for, and how to do so, and how to change them in sane ways. So far all my thinking has just convinced me that I am a mediocre architect.
No thanks.
https://docs.microsoft.com/en-us/windows/win32/com/com-class...
I’m also kinda reminded of aws lambda URLs, or I guess URLs in general.
I worked on a spike once to try and get a modern web browser embedded in a proprietary business basic language who's UI toolkit only exposed some COM/OLE stuff.
It was an unholy abomination of unmaintainable software, but it worked and worked well. It made me think a lot about how things would have looked today if ActiveX managed to keep it's foothold.
The meeting should have gone like this:
Manager: We're going to put COM on the Web!
Engineer: What about this COM function that deletes files?
Manager: We'll blacklist that.
Engineer: Or this one that reboots the PC?
Manager: Blacklist. In fact I'm assigning you the job of making the blacklist.
Engineer: I think there's an unlimited number of problematic features of COM.
Manager: Good point. OK, ActiveX is cancelled. Thanks for your time, good meeting everyone, make sure to implement PKIX correctly so we don't cause more holes there too.
My guess is that a team at Microsoft in the 90s saw the need for a cross-process runtime object system, saw Objective-C and thought to themselves "Hey, this Objective-C is pretty useful but it doesn't suck enough to be part of Win32. Lets fix that - GUIDs, GUIDs for everyone!."
There's an amusing video on youtube where Steve Jobs is demoing remote Objects on NextStep and (I think) Openstep on Windows, and having a laugh at MS since Next managed to get remote Objects working before MS got their Remote OLE (aka DCOM) working. Steve calls it Doh'LE.
The only different thing that COM had was support for scripting the objects via IDispatch, but that was such a pain (VARIANTS !) that only those really needing it bothered.
MS were also very proud of their MTS where an on the fly proxy object was created, but even that was inspired by Remote objects.
However they really jumped the shark when they made it possible to create COM objects from Visual Basic - the endless grief caused whenever a VB developer decided to change any parameters on the interface and caused all GUIDs to be regenerated and a big fat binary with all revisions of the code. They then created a tool to strip that all down to just the last version, thereby totally removing any backward compatibility - which then caused other random components to fail.
GUID were already part of DCE/RPC and Taligent.
And COM IDL is initially based on DCE IDL.
Considering that, 'spng' is different enough from 'png'.
I would call this library "spng" and the original "png"; once compiled they would be named "libspng" and "libpng".
It's different, but I think it's so close as to verge on namesquatting, especially when the description and purpose is almost identical to the original libpng --- in fact, that's why I clicked, to see what exactly this is about.
Do you pronounce it as "sping"?
> libpng compresses the image rows multiple times with different filters to optimize for size, this filtering process is not optimized.
Is there a way to disable this multiple-pass approach to get a slightly larger image that is generated faster?
> The choice of zlib also makes a big difference, stock zlib is very slow, zlib-ng is the closest to a fast zlib alternative.
I'm looking for benchmarks but I haven't found any that make it look good. This one makes it look nearly equivalent to zlib: https://quixdb.github.io/squash-benchmark/?visible-plugins=z...
Use png_set_filter() with PNG_NO_FILTERS.
> This one makes it look nearly equivalent to zlib
I don't know how squash is set up, but decompression performance depends on which library created the DEFLATE stream (see https://github.com/zlib-ng/zlib-ng/issues/326#issuecomment-4...).
If you had software that you knew spat out images with specific properties you could choose the filter by reference to what you know about the images, e.g. imagine you always produce noise anyway, the null filter is fine, if you produce a horizontal gradient you want the SUB filter, if a vertical gradient, the UP filter. But you'd need to make custom PNG code not rely on a generic library.
Why does your user experience force the user to wait?
Although you're correct, this is because it spends a lot of its time optimize for decompression time: a PNG will be compressed once, but decompressed thousands or even millions of times. It makes sense to trade off compression time for faster decompression.