Skip to main content

Hidden Art of Unclamping jxlart Artworks

Background Story

On my post about JPEG XL color analysis I mentioned that JXL stores lossy XYB in absolute, float values that can be requested during decoding process by calling pixel format data_type as JXL_TYPE_FLOAT, where out-of-gamut colors are represented with negative values.

I was recently encouraged by Amyspark from Krita to write my python Color Gamut Plotter to something native, like Qt or web-based. As I also doing hacks and sometimes contributing to Krita as well, I took the Qt path as a base. Furthermore, I'm still more comfortable coding with C/C++ rather than JS, might as well training more with Qt~ :3

And here goes the project result. I added a support for 3D plotting in xyY axes as well for the cherry on top. The 3D is not quite a performant one though, can only be plotted with very small points due to very expensive allocation of one-series-per-pixel needed for the Q3DScatter chart, because the color can't be set per data point, only per series. Thus that's why I also include a monochromatic series to plot with higher density points, although that also a bit sluggish even on my system with RTX 2080 GPU. And for the 2D plot, at first I want to utilize QChartView as well, but hitting the same issue like 3D one, only one color per series and gets exponentially more expensive as the point density rising. Finally I escorted to a custom QWidget, inspired from Krita's CIE Tongue Widget that could be seen when opening an advanced color space selector, adding more features here and there for the ease of chart manipulation. It still wasn't optimized enough and still runs on single core, but it already performs way faster than the python-based one.

Then there was an image file handling issues. The basic QImage itself is already enough to handle most of the common formats like JPEG and PNG. But c'mon, this project was (primarily) meant for JPEG XL as well. And coding wise, integrating libjxl decoding was pretty trivial. But compiling wise, was a bit of a brickwall. I don't know why building libjxl with Qt's MingW kits kept triggering a dll missing entry point or something, even with static C/CXX flags set on libjxl. And even if it does eventually built, there were random crashes when loading specific images. And then I build it separately with LLVM ucrt kits like the one used in Krita build, thus everything works smoothly. Pretty unsure how safe it is to mix the main binary that built with Qt's Mingw kit with deps binary that built with LLVM.. (but since both use gcc and g++, it should be fine I guess?). Anyway, I got it working and set the decode to always ask for unclamped float32.

So here goes the results of some #jxlart plotting, some results were quite stunning with unclamped values! Source JXL art are provided from Jon's official #jxlart page and libjxl discord server.


Credit to Jon Sneyers for the #jxlart sources!

From Jon's Official #jxlart Page

The Smoke Machine

Minor Misalignment

Inside The Flying Spaghetti Monster

Alien Message

Alien Bushfires

From Discord and Twitter

Colored Aurora

Sierpinski Foam (8K, with alpha)

Feel free to test it more! I posted the source and prebuild Win64 binaries back in the project's github page.