Implements 5bit hd gamma correction using the bit shift method for APA102 and SK9822 chipsets
This commit is contained in:
parent
3d2ab780b4
commit
a36b331703
@ -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"}
|
||||
|
47
examples/Apa102HD/Apa102HD.ino
Normal file
47
examples/Apa102HD/Apa102HD.ino
Normal file
@ -0,0 +1,47 @@
|
||||
/// @file Apa102HD.ino
|
||||
/// @brief Example showing how to use the APA102HD gamma correction.
|
||||
/// @example Apa102HD.ino
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
#define NUM_LEDS 20
|
||||
|
||||
static bool gamma_function_hit = false;
|
||||
CRGB leds_hd[NUM_LEDS] = {0}; // HD mode implies gamma.
|
||||
CRGB leds[NUM_LEDS] = {0}; // Software gamma mode.
|
||||
|
||||
|
||||
uint8_t gamma8(uint8_t x) {
|
||||
return uint8_t((uint16_t(x) * x) >> 8);
|
||||
}
|
||||
|
||||
CRGB gammaCorrect(CRGB c) {
|
||||
c.r = gamma8(c.r);
|
||||
c.g = gamma8(c.g);
|
||||
c.b = gamma8(c.b);
|
||||
return c;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(500); // power-up safety delay
|
||||
FastLED.addLeds<APA102HD, 1, 2, RGB>(leds_hd, NUM_LEDS);
|
||||
FastLED.addLeds<APA102, 3, 4, RGB>(leds, NUM_LEDS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
uint32_t t = now / 100;
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t brightness = sin8(t + i);
|
||||
CRGB c(brightness, brightness, brightness);
|
||||
leds_hd[i] = c;
|
||||
leds[i] = gammaCorrect(c);
|
||||
}
|
||||
FastLED.show();
|
||||
delay(8);
|
||||
if (!gamma_function_hit) {
|
||||
Serial.println("gamma function not hit");
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,9 @@ 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
|
||||
APA102HD, ///< APA102 LED chipset with 5-bit gamma correction
|
||||
};
|
||||
|
||||
/// Smart Matrix Library controller type
|
||||
@ -274,7 +276,9 @@ 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 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); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,7 +293,9 @@ 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 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); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,7 +310,9 @@ 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 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); }
|
||||
}
|
||||
}
|
||||
|
||||
|
216
src/chipsets.h
216
src/chipsets.h
@ -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
|
||||
> {
|
||||
};
|
||||
|
||||
|
||||
|
162
src/five_bit_hd_gamma.cpp
Normal file
162
src/five_bit_hd_gamma.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
#include "FastLED.h"
|
||||
#include "five_bit_hd_gamma.h"
|
||||
|
||||
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 nominator = 1;
|
||||
uint16_t denominator = 1;
|
||||
const uint16_t r16_const = r16;
|
||||
const uint16_t g16_const = g16;
|
||||
const uint16_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;
|
||||
}
|
||||
{
|
||||
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;
|
||||
}
|
||||
{
|
||||
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;
|
||||
}
|
||||
{
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
// 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 (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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
54
src/five_bit_hd_gamma.h
Normal 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_
|
Loading…
Reference in New Issue
Block a user