The Shape of Everything
A website mostly about Mac stuff, written by Gus Mueller
» Acorn
» Retrobatch
» Twitter
» Micro.blog
» Mastodon
» Instagram
» Github
» Maybe Pizza?
» Archives
» Feed
» Micro feed
January 5, 2019

I've got curves on the mind for some reason. And my mind was thinking- hey the curves code you've got fills everything into 256 buckets for each color channel (which acts as the LUT (Look Up Table)), and that's not going to work great for deep color images, is it?

So I dug up a 64 bit image I use for testing and ran it through the curves filter and sure enough, it looked like it had been converted to a 32 bit image. Not good at all.

So, what can I do to fix this? Well, there are a couple of options:

1: Make the LUT big enough to match the number of colors that could possibly fit into each channel. This would mean I would have to create a different sized LUT for each 32/64/128 bit image. That means more code. It also means more work for the CPU to do to fill up all those buckets as well as calculate the values based on the curve paths.

2: Change the code so that it doesn't take a LUT, but rather the vector data for the curves and then calculate the color changes on the fly based off that data, but on the GPU. This sounds like an awesome problem to solve, but I'm not sure it would be worth the time spent.

Honestly, I didn't want to do either of those solutions, but I was pretty resigned to implementing method #1. But I kept on thinking about it and went climbing for a bit and came back with another idea.

3: Keep the existing code, but use bilinear interpolation when accessing values from the LUT to figure out the color shifts. This is an extremely easy change and is probably about 98% as good as the previous two options. And it's certainly going to be way better than the current situation.

So I made that change, noticed nothing different, and then realized I was calling floor() in the filter kernel, completely the interpolation I was trying to use. So I got to delete code, and it made things better.

Here are the results. Old on the left, new on the right. Click on the image to make it bigger, because you're not going to see any differences at the size it's currently at.

Deep color curves.

You'll see a lot more banding on the left image than you do on the right image. And this image is actually a 24 bit PNG, where on my Mac it's a 64 bit PNG running through a wide gamut monitor. The differences are much more drastic here, and there's no perceivable banding at all for the fixed one.

What's next for curves? I'm not sure, maybe I'm done for the moment? I'd like to throw the alpha channel in there as well, but outside of a few interesting cases, it probably wouldn't be worth it.

Both these changes, and the changes I mentioned in my previous post are now availabe on Acorn's latest builds page.