Next-Flip / Momentum-Firmware

🐬 Feature-rich, stable and customizable Flipper Firmware
https://momentum-fw.dev
GNU General Public License v3.0
4.56k stars 183 forks source link

Perceptually-Uniform RGB Backlight Hues / Animation #96

Closed zacharyweiss closed 6 months ago

zacharyweiss commented 6 months ago

Describe the enhancement you're suggesting.

A suggestion, if acceptable computational- / memory-intensity-wise. Orbit / interpolate over a perceptually uniform colorspace a la OKLAB instead of over the RGB/HSV values, as the latter (the current approach) leads to perceptual peaks/valleys in lightness. On-the-fly conversion requires some floating-pt math (might be intensive for what's intended as a background process?); alternatively, lookup tables could be precomputed for speed, if there's sufficient memory.

Code for conversion to/from linear sRGB listed in the above-linked post; would need minor modification / composition with sRGB<>RGB565/888 for conversion to/from RGB565/888:

struct Lab {float L; float a; float b;};
struct RGB {float r; float g; float b;};

Lab linear_srgb_to_oklab(RGB c) {
    float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
    float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
    float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;

    // these cube-roots are likely to be the worst offender performance-wise
    // can forgo by simply defining the starting point in OKLAB for the wave/static fxn,
    // hence would never need to convert RGB -> OKLAB; 
    // only the reverse which involves cubing
    float l_ = cbrtf(l);
    float m_ = cbrtf(m);
    float s_ = cbrtf(s);

    return {
        0.2104542553f*l_ + 0.7936177850f*m_ - 0.0040720468f*s_,
        1.9779984951f*l_ - 2.4285922050f*m_ + 0.4505937099f*s_,
        0.0259040371f*l_ + 0.7827717662f*m_ - 0.8086757660f*s_,
    };
}

// interpolate / walk across OKLAB values, before converting back to RGB
// in current implementation, it seems you simply multiply the hue of HSV
// by a constant and a variable step.
// Here, the perceptually uniform equivalent would be holding L and some radius R constant, 
// while setting a & b equal to R*sin & R*cos of your step (I think...)
// (drawing a unit circle over the uniform colorspace)

RGB oklab_to_linear_srgb(Lab c) {
    float l_ = c.L + 0.3963377774f * c.a + 0.2158037573f * c.b;
    float m_ = c.L - 0.1055613458f * c.a - 0.0638541728f * c.b;
    float s_ = c.L - 0.0894841775f * c.a - 1.2914855480f * c.b;

    float l = l_*l_*l_;
    float m = m_*m_*m_;
    float s = s_*s_*s_;

    return {
        +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
        -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
        -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
    };
}

Thoughts? Feel free to close if too intensive / not a significant enough experiential benefit.

Anything else?

No response

zacharyweiss commented 6 months ago

Asides:

Willy-JL commented 6 months ago

We will soon have to fully redo the rgb backlight stuff since @Z3BRO made a new rgb thing for clear cases and fully disregarded the improvements we had to rgb backlight and rainbow settings in momentum, so anything related to this color stuff will have to wait for that rework. Interesting stuff however, I'm not experienced in this myself but might be something to look into z3bro?

Willy-JL commented 6 months ago

Also something to keep in mind is that from what I've heard, the rgb backlight are absolutely unreliable in terms of color accuracy and shown colors vary abysmally between different installed mods, so might not even be worth the hassle... just thought it would be worth mentioning, but then again I don't know and don't even have an rgb backlight

zacharyweiss commented 6 months ago

All good points! I'll close this given the above; can always reopen if it seems worthwhile in light of (no pun intended) reworked RGB HW (& SW support).

Z3BRO commented 6 months ago

Honestly, this is excellent stuff, and it would be super cool to implement. The only issue is that the SK6805 1515 Neopixels used for the RGB Backlight and the Internal RGB are extremely color inaccurate. Even if the LEDs were more color accurate, I feel like these changes would go pretty unnoticed, as this mod is really only about "ooooo pretty colors."

zacharyweiss commented 6 months ago

It's such a fun nerd-sniping hole to fall down; there's so much fascinating work in the space. The primary experiential benefit would less so be the colors themselves, and moreso on the consistency of apparent brightness as the hues change, but I definitely agree that it's likely in the noise / not worth the effort with the current degree of LED-accuracy.

If looking for an excuse to mess around with color math though, it could be worthwhile to impl for the VGM / RGB-over-RPC feature, as that gets played over an actual LCD/similar display, and more importantly: relies on the Flipper to calculate the color gradients across the screen (whereas in reality obviously the gradients are left to the literal physics of light diffusion & mixing, and the above changes would've just been for consistent brightness across the LEDs / across time)