Skip to content

Fix issue with banding in the gradient map filter

Deif Lou requested to merge deiflou/krita:deiflou/gradientmap_intensity into master

This MR tries to solve bug 463585: some noticeable banding appears when using the gradient map filter.

There was two issues:

  • There was some banding in 8 bits. For example, if we made a black to white gradient on a layer and then we applied the gradient map with the same black to white gradient to that layer, every gray color on the layer should be mapped to the same gray color on the gradient (kind of an identity function since the black to white gradient was used). But there was some banding. This was due to the intensity8 function needing rounding.

    I changed the implementation of that function to use an integer version that is also more accurate, due to floating point errors on the old implementation. For example, if we had some rgb color like (255, 253, 243) then the formula: 255 * 0.30 + 253 * 0.59 + 243 * 0.11 gives 252.4999999999... when it should be 252.5. Even when adding 0.5 to round (qRound), the result is 252.999999..., which gets floored to 252 when casting to int. The result should be 253. The integer version does not have those errors. It is (p->red * 30 + p->green * 59 + p->blue * 11 + 50) / 100. The + 50 is there for rounding. What I don't know is whether that's less performant, since it uses a division.

  • The other issue which produced banding was present when the image had a higher depth. This time the previous issue is still relevant, but even when it was fixed the banding persisted. It was due to the intensity8 function using the toQColor function to get a rgb color and then converting that to grayscale. toQColor converts the original color to 8 bit rgb. So when that was used to generate the intensity value, there was no enough range, and the banding appeared. So I implemented a new intensityF function which returns a double in the range [0, 1]. This uses a new toQColor16 function that converts the original color to a 16 bit rgb QColor. QColor uses 16 bits internally, so that is perfectly fine. with a 16 bit QColor functions like redF have more precision, so we can generate an intensity value that also has more precision to avoid the banding. I had to add some things to LcmsColorSpace. Let me know if they are ok because I'm not confident enough touching such files.


Formalities Checklist

  • I confirmed this builds.
  • I confirmed Krita ran and the relevant functions work.
  • I tested the relevant unit tests and can confirm they are not broken. (If not possible, don't hesitate to ask for help!)
  • I made sure my commits build individually and have good descriptions as per KDE guidelines.
  • I made sure my code conforms to the standards set in the HACKING file.
  • I can confirm the code is licensed and attributed appropriately, and that unattributed code is mine, as per KDE Licensing Policy.

Merge request reports