Merge pull request #1546 from zackees/apa102-hd-gamma

Implements "High Bit Depth Gamma Correction Algorithm for APA102/Dotstar LEDs" for APA102 and SK9822 chipsets
This commit is contained in:
Mark Kriegsman 2023-10-25 13:21:07 -04:00 committed by GitHub
commit 3a03742a09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 441 additions and 67 deletions

View File

@ -17,7 +17,7 @@
set -eou pipefail
# List of examples that will be compiled by default
EXAMPLES=${EXAMPLES:-"Blink ColorPalette ColorTemperature Cylon DemoReel100
EXAMPLES=${EXAMPLES:-"Apa102HD Blink ColorPalette ColorTemperature Cylon DemoReel100
Fire2012 FirstLight Multiple/MultipleStripsInOneArray
Multiple/ArrayOfLedArrays Noise NoisePlayground NoisePlusPalette Pacifica
Pride2015 RGBCalibrate RGBSetDemo TwinkleFox XYMatrix"}

View File

@ -0,0 +1,87 @@
/// @file Apa102HD.ino
/// @brief Example showing how to use the APA102HD gamma correction.
///
/// In this example we compare two strips of LEDs.
/// One strip is in HD mode, the other is in software gamma mode.
///
/// Each strip is a linear ramp of brightnesses, from 0 to 255.
/// Showcasing all the different brightnesses.
///
/// Why do we love gamma correction? Gamma correction more closely
/// matches how humans see light. Led values are measured in fractions
/// of max power output (1/255, 2/255, etc.), while humans see light
/// in a logarithmic way. Gamma correction converts to this eye friendly
/// curve. Gamma correction wants a LED with a high bit depth. The APA102
/// gives us the standard 3 components (red, green, blue) with 8 bits each, it
/// *also* has a 5 bit brightness component. This gives us a total of 13 bits,
/// which allows us to achieve a higher dynamic range. This means deeper fades.
///
/// Example:
/// CRGB leds[NUM_LEDS] = {0};
/// void setup() {
/// FastLED.addLeds<
/// APA102HD, // <--- This selects HD mode.
/// STRIP_0_DATA_PIN,
/// STRIP_0_CLOCK_PIN,
/// RGB
/// >(leds, NUM_LEDS);
/// }
#include <Arduino.h>
#include <FastLED.h>
#include <lib8tion.h>
#define NUM_LEDS 20
// uint8_t DATA_PIN, uint8_t CLOCK_PIN,
#define STRIP_0_DATA_PIN 1
#define STRIP_0_CLOCK_PIN 2
#define STRIP_1_DATA_PIN 3
#define STRIP_1_CLOCK_PIN 4
CRGB leds_hd[NUM_LEDS] = {0}; // HD mode implies gamma.
CRGB leds[NUM_LEDS] = {0}; // Software gamma mode.
// This is the regular gamma correction function that we used to have
// to do. It's used here to showcase the difference between APA102HD
// mode which does the gamma correction for you.
CRGB software_gamma(const CRGB& in) {
CRGB out;
// dim8_raw are the old gamma correction functions.
out.r = dim8_raw(in.r);
out.g = dim8_raw(in.g);
out.b = dim8_raw(in.b);
return out;
}
void setup() {
delay(500); // power-up safety delay
// Two strips of LEDs, one in HD mode, one in software gamma mode.
FastLED.addLeds<APA102HD, STRIP_0_DATA_PIN, STRIP_0_CLOCK_PIN, RGB>(leds_hd, NUM_LEDS);
FastLED.addLeds<APA102, STRIP_1_DATA_PIN, STRIP_1_CLOCK_PIN, RGB>(leds, NUM_LEDS);
}
uint8_t wrap_8bit(int i) {
// Module % operator here wraps a large "i" so that it is
// always in [0, 255] range when returned. For example, if
// "i" is 256, then this will return 0. If "i" is 257
// then this will return 1. No matter how big the "i" is, the
// output range will always be [0, 255]
return i % 256;
}
void loop() {
// Draw a a linear ramp of brightnesses to showcase the difference between
// the HD and non-HD mode.
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t brightness = map(i, 0, NUM_LEDS - 1, 0, 255);
CRGB c(brightness, brightness, brightness); // Just make a shade of white.
leds_hd[i] = c; // The APA102HD leds do their own gamma correction.
CRGB c_gamma_corrected = software_gamma(c);
leds[i] = c_gamma_corrected; // Set the software gamma corrected
// values to the other strip.
}
FastLED.show(); // All leds are now written out.
delay(8); // Wait 8 milliseconds until the next frame.
}

View File

@ -91,7 +91,10 @@ enum ESPIChipsets {
P9813, ///< P9813 LED chipset
APA102, ///< APA102 LED chipset
SK9822, ///< SK9822 LED chipset
DOTSTAR ///< APA102 LED chipset alias
SK9822HD, ///< SK9822 LED chipset with 5-bit gamma correction
DOTSTAR, ///< APA102 LED chipset alias
DOTSTARHD, ///< APA102HD LED chipset alias
APA102HD, ///< APA102 LED chipset with 5-bit gamma correction
};
/// Smart Matrix Library controller type
@ -278,7 +281,10 @@ public:
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTARHD:
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
@ -293,7 +299,10 @@ public:
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTARHD:
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
@ -308,7 +317,10 @@ public:
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTARHD:
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}

View File

@ -3,6 +3,7 @@
#include "FastLED.h"
#include "pixeltypes.h"
#include "five_bit_hd_gamma.h"
/// @file chipsets.h
/// Contains the bulk of the definitions for the various LED chipsets supported.
@ -214,13 +215,35 @@ protected:
/// @tparam CLOCK_PIN the clock pin for these LEDs
/// @tparam RGB_ORDER the RGB ordering for these LEDs
/// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(12)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)>
template <
uint8_t DATA_PIN, uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(12),
FiveBitGammaCorrectionMode GAMMA_CORRECTION_MODE = kFiveBitGammaCorrectionMode_Null,
uint32_t START_FRAME = 0x00000000,
uint32_t END_FRAME = 0xFF000000
>
class APA102Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
void endBoundary(int nLeds) { int nDWords = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nDWords--); }
void startBoundary() {
mSPI.writeWord(START_FRAME >> 16);
mSPI.writeWord(START_FRAME & 0xFFFF);
}
void endBoundary(int nLeds) {
int nDWords = (nLeds/32);
const uint8_t b0 = uint8_t(END_FRAME >> 24 & 0x000000ff);
const uint8_t b1 = uint8_t(END_FRAME >> 16 & 0x000000ff);
const uint8_t b2 = uint8_t(END_FRAME >> 8 & 0x000000ff);
const uint8_t b3 = uint8_t(END_FRAME >> 0 & 0x000000ff);
do {
mSPI.writeByte(b0);
mSPI.writeByte(b1);
mSPI.writeByte(b2);
mSPI.writeByte(b3);
} while(nDWords--);
}
inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
#ifdef FASTLED_SPI_BYTE_ONLY
@ -237,6 +260,15 @@ class APA102Controller : public CPixelLEDController<RGB_ORDER> {
#endif
}
inline void write2Bytes(uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
#ifdef FASTLED_SPI_BYTE_ONLY
mSPI.writeByte(b1);
mSPI.writeByte(b2);
#else
mSPI.writeWord(uint16_t(b1) << 8 | b2);
#endif
}
public:
APA102Controller() {}
@ -247,9 +279,26 @@ public:
protected:
/// @copydoc CPixelLEDController::showPixels()
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
switch (GAMMA_CORRECTION_MODE) {
case kFiveBitGammaCorrectionMode_Null: {
showPixelsDefault(pixels);
break;
}
case kFiveBitGammaCorrectionMode_BitShift: {
showPixelsGammaBitShift(pixels);
break;
}
}
}
uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2();
private:
static inline void getGlobalBrightnessAndScalingFactors(
PixelController<RGB_ORDER>& pixels,
uint8_t* out_s0, uint8_t* out_s1, uint8_t* out_s2, uint8_t* out_brightness) {
uint8_t s0 = pixels.getScale0();
uint8_t s1 = pixels.getScale1();
uint8_t s2 = pixels.getScale2();
#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
const uint16_t maxBrightness = 0x1F;
uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1;
@ -259,10 +308,23 @@ protected:
#else
const uint8_t brightness = 0x1F;
#endif
*out_s0 = s0;
*out_s1 = s1;
*out_s2 = s2;
*out_brightness = static_cast<uint8_t>(brightness);
}
// Legacy showPixels implementation.
inline void showPixelsDefault(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
uint8_t s0, s1, s2, global_brightness;
getGlobalBrightnessAndScalingFactors(pixels, &s0, &s1, &s2, &global_brightness);
startBoundary();
while (pixels.has(1)) {
writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2));
uint8_t r = pixels.loadAndScale0(0, s0);
uint8_t g = pixels.loadAndScale1(0, s1);
uint8_t b = pixels.loadAndScale2(0, s2);
writeLed(global_brightness, r, g, b);
pixels.stepDithering();
pixels.advanceData();
}
@ -272,72 +334,96 @@ protected:
mSPI.release();
}
inline void showPixelsGammaBitShift(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
uint8_t s0, s1, s2, global_brightness;
getGlobalBrightnessAndScalingFactors(pixels, &s0, &s1, &s2, &global_brightness);
startBoundary();
while (pixels.has(1)) {
uint8_t r = pixels.loadAndScale0(0, s0);
uint8_t g = pixels.loadAndScale1(0, s1);
uint8_t b = pixels.loadAndScale2(0, s2);
uint8_t brightness = 0;
five_bit_hd_gamma_bitshift(r, g, b, &r, &g, &b, &brightness);
if (global_brightness >= 0x1F) {
// 5-bit mix.
brightness = static_cast<uint8_t>(
(uint16_t(brightness) * global_brightness)
/ 0x1F
);
}
writeLed(brightness, r, g, b);
pixels.stepDithering();
pixels.advanceData();
}
endBoundary(pixels.size());
mSPI.waitFully();
mSPI.release();
}
};
/// SK9822 controller class.
template <
uint8_t DATA_PIN,
uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
>
class APA102ControllerHD : public APA102Controller<
DATA_PIN,
CLOCK_PIN,
RGB_ORDER,
SPI_SPEED,
kFiveBitGammaCorrectionMode_BitShift,
uint32_t(0x00000000),
uint32_t(0x00000000)> {
public:
APA102ControllerHD() = default;
APA102ControllerHD(const APA102ControllerHD&) = delete;
};
/// SK9822 controller class. It's exactly the same as the APA102Controller protocol but with a different END_FRAME and default SPI_SPEED.
/// @tparam DATA_PIN the data pin for these LEDs
/// @tparam CLOCK_PIN the clock pin for these LEDs
/// @tparam RGB_ORDER the RGB ordering for these LEDs
/// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(24)
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(24)>
class SK9822Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); }
void endBoundary(int nLeds) { int nLongWords = (nLeds/32); do { mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nLongWords--); }
inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
#ifdef FASTLED_SPI_BYTE_ONLY
mSPI.writeByte(0xE0 | brightness);
mSPI.writeByte(b0);
mSPI.writeByte(b1);
mSPI.writeByte(b2);
#else
uint16_t b = 0xE000 | (brightness << 8) | (uint16_t)b0;
mSPI.writeWord(b);
uint16_t w = b1 << 8;
w |= b2;
mSPI.writeWord(w);
#endif
}
public:
SK9822Controller() {}
virtual void init() {
mSPI.init();
}
protected:
/// @copydoc CPixelLEDController::showPixels()
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2();
#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1
const uint16_t maxBrightness = 0x1F;
uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1;
s0 = (maxBrightness * s0 + (brightness >> 1)) / brightness;
s1 = (maxBrightness * s1 + (brightness >> 1)) / brightness;
s2 = (maxBrightness * s2 + (brightness >> 1)) / brightness;
#else
const uint8_t brightness = 0x1F;
#endif
startBoundary();
while (pixels.has(1)) {
writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2));
pixels.stepDithering();
pixels.advanceData();
}
endBoundary(pixels.size());
mSPI.waitFully();
mSPI.release();
}
template <
uint8_t DATA_PIN,
uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
>
class SK9822Controller : public APA102Controller<
DATA_PIN,
CLOCK_PIN,
RGB_ORDER,
SPI_SPEED,
kFiveBitGammaCorrectionMode_Null,
0x00000000,
0x00000000
> {
};
/// SK9822 controller class. It's exactly the same as the APA102Controller protocol but with a different END_FRAME and default SPI_SPEED.
/// @tparam DATA_PIN the data pin for these LEDs
/// @tparam CLOCK_PIN the clock pin for these LEDs
/// @tparam RGB_ORDER the RGB ordering for these LEDs
/// @tparam SPI_SPEED the clock divider used for these LEDs. Set using the ::DATA_RATE_MHZ / ::DATA_RATE_KHZ macros. Defaults to ::DATA_RATE_MHZ(24)
template <
uint8_t DATA_PIN,
uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
>
class SK9822ControllerHD : public APA102Controller<
DATA_PIN,
CLOCK_PIN,
RGB_ORDER,
SPI_SPEED,
kFiveBitGammaCorrectionMode_BitShift,
0x00000000,
0x00000000
> {
};

135
src/five_bit_hd_gamma.cpp Normal file
View File

@ -0,0 +1,135 @@
#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
__attribute__((weak))
void five_bit_hd_gamma_function(
uint8_t r8, uint8_t g8, uint8_t b8,
uint16_t* r16, uint16_t* g16, uint16_t* b16) {
*r16 = uint16_t(r8) * r8;
*g16 = uint16_t(g8) * g8;
*b16 = uint16_t(b8) * b8;
}
__attribute__((weak))
void five_bit_hd_gamma_bitshift(
uint8_t r8, uint8_t g8, uint8_t b8,
uint8_t* out_r8,
uint8_t* out_g8,
uint8_t* out_b8,
uint8_t* out_power_5bit) {
// Step 1: Gamma Correction
uint16_t r16, g16, b16;
five_bit_hd_gamma_function(r8, g8, b8, &r16, &g16, &b16);
// Step 2: Initialize 5-bit brightness.
// Note: we only get 5 levels of brightness
uint8_t v8 = 31;
uint16_t numerator = 1;
uint16_t denominator = 1;
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 {
// 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;
}
if (g16_const * 31 > max_value) {
break;
}
if (b16_const * 31 > max_value) {
break;
}
numerator = 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;
}
numerator = 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;
}
numerator = 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;
}
numerator = 31 * 15 * 7 * 3;
v8 = 1;
} while(false);
r16 = uint16_t(r16_const * numerator / denominator);
g16 = uint16_t(g16_const * numerator / denominator);
b16 = uint16_t(b16_const * numerator / 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.
if (r8 < 9 && r16 > 0) {
r8_final = r8;
}
if (g8 < 9 && g16 > 0) {
g8_final = g8;
}
if (b8 < 9 && b16 > 0) {
b8_final = b8;
}
}
#endif
// Step 5: Output
*out_r8 = r8_final;
*out_g8 = g8_final;
*out_b8 = b8_final;
*out_power_5bit = v8;
}
FASTLED_NAMESPACE_END

54
src/five_bit_hd_gamma.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef _FIVE_BIT_HD_GAMMA_H_
#define _FIVE_BIT_HD_GAMMA_H_
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
enum FiveBitGammaCorrectionMode {
kFiveBitGammaCorrectionMode_Null = 0,
kFiveBitGammaCorrectionMode_BitShift = 1
};
// Applies gamma correction for the RGBV(8, 8, 8, 5) color space, where
// the last byte is the brightness byte at 5 bits.
// To override this five_bit_hd_gamma_bitshift function just define
// your own version anywhere in your project.
// Example:
// FASTLED_NAMESPACE_BEGIN
// void five_bit_hd_gamma_bitshift(
// uint8_t r8, uint8_t g8, uint8_t b8,
// uint8_t* out_r8,
// uint8_t* out_g8,
// uint8_t* out_b8,
// uint8_t* out_power_5bit) {
// cout << "hello world\n";
// }
// FASTLED_NAMESPACE_END
void five_bit_hd_gamma_bitshift(
uint8_t r8, uint8_t g8, uint8_t b8,
uint8_t* out_r8,
uint8_t* out_g8,
uint8_t* out_b8,
uint8_t* out_power_5bit) __attribute__((weak));
// Simple gamma correction function that converts from
// 8-bit color component and converts it to gamma corrected 16-bit
// color component. Fast and no memory overhead!
// To override this function just define your own version
// anywhere in your project.
// Example:
// FASTLED_NAMESPACE_BEGIN
// void five_bit_hd_gamma_function(
// uint8_t r8, uint8_t g8, uint8_t b8,
// uint16_t* r16, uint16_t* g16, uint16_t* b16) {
// cout << "hello world\n";
// }
// FASTLED_NAMESPACE_END
void five_bit_hd_gamma_function(
uint8_t r8, uint8_t g8, uint8_t b8,
uint16_t* r16, uint16_t* g16, uint16_t* b16) __attribute__((weak));
FASTLED_NAMESPACE_END
#endif // _FIVE_BIT_HD_GAMMA_H_