Implements 5bit hd gamma correction using the bit shift method for APA102 and SK9822 chipsets

This commit is contained in:
zackees 2023-09-29 14:10:35 -07:00 committed by zackees
parent a36b331703
commit 73192ff41b

View File

@ -1,6 +1,10 @@
#include "FastLED.h"
#include "five_bit_hd_gamma.h"
#ifndef FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP
#define FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP 1
#endif
FASTLED_NAMESPACE_BEGIN
@ -31,113 +35,81 @@ void five_bit_hd_gamma_bitshift(
uint16_t nominator = 1;
uint16_t denominator = 1;
const uint16_t r16_const = r16;
const uint16_t g16_const = g16;
const uint16_t b16_const = b16;
const uint32_t r16_const = r16;
const uint32_t g16_const = g16;
const uint32_t b16_const = b16;
// Step 3: Bit Shifting Loop, can probably replaced with a
// single pass bit-twiddling hack.
do {
{
uint32_t next_r16 = r16 * 31 / 15;
uint32_t next_g16 = g16 * 31 / 15;
uint32_t next_b16 = b16 * 31 / 15;
if (next_r16 > 0xffff) {
break;
}
if (next_g16 > 0xffff) {
break;
}
if (next_b16 > 0xffff) {
break;
}
nominator = nominator * 31;
denominator = denominator * 15;
v8 = v8 >> 1;
r16 = next_r16;
g16 = next_g16;
b16 = next_b16;
// Note that to avoid slow divisions, we multiply the max_value
// by the denominator.
uint32_t max_value = 0xfffful * 15;
if (r16_const * 31 > max_value) {
break;
}
{
uint32_t next_r16 = r16 * 15 / 7;
uint32_t next_g16 = g16 * 15 / 7;
uint32_t next_b16 = b16 * 15 / 7;
if (next_r16 > 0xffff) {
break;
}
if (next_g16 > 0xffff) {
break;
}
if (next_b16 > 0xffff) {
break;
}
nominator = nominator * 15;
denominator = denominator * 7;
v8 = v8 >> 1;
r16 = next_r16;
g16 = next_g16;
b16 = next_b16;
if (g16_const * 31 > max_value) {
break;
}
{
uint32_t next_r16 = r16 * 7 / 3;
uint32_t next_g16 = g16 * 7 / 3;
uint32_t next_b16 = b16 * 7 / 3;
if (next_r16 > 0xffff) {
break;
}
if (next_g16 > 0xffff) {
break;
}
if (next_b16 > 0xffff) {
break;
}
nominator = nominator * 7;
denominator = denominator * 3;
v8 = v8 >> 1;
r16 = next_r16;
g16 = next_g16;
b16 = next_b16;
if (b16_const * 31 > max_value) {
break;
}
{
uint32_t next_r16 = r16 * 3;
uint32_t next_g16 = g16 * 3;
uint32_t next_b16 = b16 * 3;
if (next_r16 > 0xffff) {
break;
}
if (next_g16 > 0xffff) {
break;
}
if (next_b16 > 0xffff) {
break;
}
nominator = nominator * 3;
v8 = v8 >> 1;
r16 = next_r16;
g16 = next_g16;
b16 = next_b16;
nominator = 31;
denominator = 15;
v8 = 15;
max_value = 0xfffful * 15 * 7;
if (r16_const * 31 * 15 > max_value) {
break;
}
if (g16_const * 31 * 15 > max_value) {
break;
}
if (b16_const * 31 * 15 > max_value) {
break;
}
nominator = 31 * 15;
denominator = 15 * 7;
v8 = 7;
max_value = 0xfffful * 15 * 7 * 3;
if (r16_const * 31 * 15 * 7 > max_value) {
break;
}
if (g16_const * 31 * 15 * 7 > max_value) {
break;
}
if (b16_const * 31 * 15 * 7 > max_value) {
break;
}
nominator = 31 * 15 * 7;
denominator = 15 * 7 * 3;
v8 = 3;
max_value = 0xfffful * 15 * 7 * 3;
if (r16_const * 31 * 15 * 7 * 3 > max_value) {
break;
}
if (g16_const * 31 * 15 * 7 * 3 > max_value) {
break;
}
if (b16_const * 31 * 15 * 7 * 3 > max_value) {
break;
}
nominator = 31 * 15 * 7 * 3;
v8 = 1;
} while(false);
r16 = r16_const * nominator / denominator;
g16 = g16_const * nominator / denominator;
b16 = b16_const * nominator / denominator;
// protect against overflow
if (r16 > 0xffff) {
r16 = 0xffff;
}
if (g16 > 0xffff) {
g16 = 0xffff;
}
if (b16 > 0xffff) {
b16 = 0xffff;
}
r16 = uint16_t(r16_const * nominator / denominator);
g16 = uint16_t(g16_const * nominator / denominator);
b16 = uint16_t(b16_const * nominator / denominator);
// Step 4: Conversion Back to 8-bit.
uint8_t r8_final = (r8 == 255 && uint8_t(r16 >> 8) >= 254) ? 255 : uint8_t(r16 >> 8);
uint8_t g8_final = (g8 == 255 && uint8_t(g16 >> 8) >= 254) ? 255 : uint8_t(g16 >> 8);
uint8_t b8_final = (b8 == 255 && uint8_t(b16 >> 8) >= 254) ? 255 : uint8_t(b16 >> 8);
#if FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP == 1
if (v8 == 1) {
// Linear tuning for the lowest possible brightness. x=y until
// the intersection point at 9.
@ -151,6 +123,7 @@ void five_bit_hd_gamma_bitshift(
b8_final = b8;
}
}
#endif
// Step 5: Output
*out_r8 = r8_final;