From e98acd5c40ff2c9d62560837b38ecead74eda201 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 30 Apr 2019 23:03:08 +0200 Subject: [PATCH] Changed gamma correction to adaptative resolution from 8 to 11 bits --- sonoff/xdrv_04_light.ino | 115 +++++++++++++++++++++++++++++++++------ sonoff/xplg_ws2812.ino | 8 +-- 2 files changed, 101 insertions(+), 22 deletions(-) diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index f9cc47fff..1292f25e2 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -102,23 +102,70 @@ struct LCwColor { const uint8_t MAX_FIXED_COLD_WARM = 4; const LCwColor kFixedColdWarm[MAX_FIXED_COLD_WARM] PROGMEM = { 0,0, 255,0, 0,255, 128,128 }; -const uint8_t ledTable[] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, - 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, - 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, - 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, - 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, - 33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, - 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, - 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, - 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98, 99, -101,102,104,105,107,108,110,111,113,114,116,117,119,121,122,124, -125,127,129,130,132,134,135,137,139,141,142,144,146,148,150,151, -153,155,157,159,161,163,165,166,168,170,172,174,176,178,180,182, -184,186,189,191,193,195,197,199,201,204,206,208,210,212,215,217, -219,221,224,226,228,231,233,235,238,240,243,245,248,250,253,255 }; +// New version of Gamma correction table, with adaptative resolution +// from 11 bits (lower values) to 8 bits (upper values). +// We're using the fact that lower values are small and can fit within 8 bits +// To save flash space, the array is only 8 bits uint +const uint8_t _ledTable[] = { + // 11 bits resolution + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, // 11 bits, 0..2047 + 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, // 11 bits, 0..2047 + 7, 8, 8, 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, // 11 bits, 0..2047 + 20, 21, 22, 24, 25, 26, 28, 29, 30, 32, 33, 35, 37, 38, 40, 42, // 11 bits, 0..2047 + // 10 bits resolution + 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 36, 37, 38, 39, // 10 bits, 0..1023 + 41, 42, 44, 45, 47, 48, 50, 51, 53, 55, 56, 58, 60, 62, 64, 65, // 10 bits, 0..1023 + 67, 69, 71, 73, 75, 78, 80, 82, 84, 86, 89, 91, 93, 96, 98,101, // 10 bits, 0..1023 + 103,106,108,111,114,116,119,122,125,128,131,134,137,140,143,146, // 10 bits, 0..1023 + // 9 bits resolution + 75, 77, 78, 80, 82, 84, 85, 87, 89, 91, 93, 94, 96, 98,100,102, // 9 bits, 0..511 + 104,106,108,110,112,115,117,119,121,123,125,128,130,132,135,137, // 9 bits, 0..511 + 140,142,144,147,149,152,155,157,160,163,165,168,171,173,176,179, // 9 bits, 0..511 + 182,185,188,191,194,197,200,203,206,209,212,215,219,222,225,229, // 9 bits, 0..511 + // 8 bits resolution + 116,118,120,121,123,125,127,128,130,132,134,136,138,139,141,143, // 8 bits, 0..255 + 145,147,149,151,153,155,157,159,161,163,165,168,170,172,174,176, // 8 bits, 0..255 + 178,181,183,185,187,190,192,194,197,199,201,204,206,209,211,214, // 8 bits, 0..255 + 216,219,221,224,226,229,232,234,237,240,242,245,248,250,253,255 // 8 bits, 0..255 +}; + +// For reference, below are the computed gamma tables, via ledGamma() +// for 8 bits output: +// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, +// 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, +// 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, +// 11, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14, 15, 15, 16, 16, 17, +// 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, +// 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, +// 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 47, 48, 49, 50, 51, +// 52, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, +// 70, 71, 72, 74, 75, 76, 78, 79, 80, 82, 83, 84, 86, 87, 88, 90, +// 91, 93, 94, 96, 97, 99,100,102,103,105,106,108,110,111,113,115, +//116,118,120,121,123,125,127,128,130,132,134,136,138,139,141,143, +//145,147,149,151,153,155,157,159,161,163,165,168,170,172,174,176, +//178,181,183,185,187,190,192,194,197,199,201,204,206,209,211,214, +//216,219,221,224,226,229,232,234,237,240,242,245,248,250,253,255 +// +// and for 10 bits output: +// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, +// 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, +// 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 18, 19, 19, 20, 21, +// 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 36, 37, 38, 39, +// 41, 42, 44, 45, 47, 48, 50, 51, 53, 55, 56, 58, 60, 62, 64, 65, +// 67, 69, 71, 73, 75, 78, 80, 82, 84, 86, 89, 91, 93, 96, 98,101, +//103,106,108,111,114,116,119,122,125,128,131,134,137,140,143,146, +//151,155,157,161,165,169,171,175,179,183,187,189,193,197,201,205, +//209,213,217,221,225,231,235,239,243,247,251,257,261,265,271,275, +//281,285,289,295,299,305,311,315,321,327,331,337,343,347,353,359, +//365,371,377,383,389,395,401,407,413,419,425,431,439,445,451,459, +//467,475,483,487,495,503,511,515,523,531,539,547,555,559,567,575, +//583,591,599,607,615,623,631,639,647,655,663,675,683,691,699,707, +//715,727,735,743,751,763,771,779,791,799,807,819,827,839,847,859, +//867,879,887,899,907,919,931,939,951,963,971,983,995,1003,1015,1023 + uint8_t light_entry_color[5]; uint8_t light_current_color[5]; @@ -747,6 +794,38 @@ public: LightStateClass light_state = LightStateClass(); LightControllerClass light_controller = LightControllerClass(light_state); +/*********************************************************************************************\ + * Gamma correction +\*********************************************************************************************/ +// uint16_t ledGamma(uint8_t v, uint16_t max_range = 255) { +// uint16_t slot = 3 - (v / 64); // 0..3 +// uint16_t uncorrected_value = _ledTable[v]; +// uint16_t range = (0x100 << slot) - 1; +// return changeUIntScale(uncorrected_value, 0, range, 0, max_range); +// } +// bits can be 8,9,10,11 +uint16_t ledGamma(uint8_t v, uint16_t bits_out = 8) { + uint16_t result; + // bits_resolution: the resolution of _ledTable[v], between 8 and 11 + uint32_t bits_resolution = 11 - (v / 64); // 8..11 + int32_t bits_correction = bits_out - bits_resolution; // -3..3 + uint32_t uncorrected_value = _ledTable[v]; // 0..255 + if (0 == bits_correction) { + // we already match the required resolution, no change + result = uncorrected_value; + } else if (bits_correction > 0) { + // the output resolution is higher than our value, we need to extrapolate + // we shift by bits_correction, and force last bits to 1 + uint32_t bits_mask = (1 << bits_correction) - 1; // 1, 3, 7 + result = (uncorrected_value << bits_correction) | bits_mask; + } else { // bits_correction < 0 + // our resolution is too high, we need to remove bits + // we add 1, 3 or 7 to force rouding to the nearest high value + uint32_t bits_mask = (1 << -bits_correction) - 1; // 1, 3, 7 + result = ((uncorrected_value + bits_mask) >> -bits_correction); + } + return result; +} #ifdef USE_ARILUX_RF /*********************************************************************************************\ @@ -1531,7 +1610,7 @@ void LightAnimate(void) for (uint8_t i = 0; i < light_subtype; i++) { light_last_color[i] = light_new_color[i]; cur_col[i] = light_last_color[i]*Settings.rgbwwTable[i]/255; - cur_col[i] = (Settings.light_correction) ? ledTable[cur_col[i]] : cur_col[i]; + cur_col[i] = (Settings.light_correction) ? ledGamma(cur_col[i]) : cur_col[i]; } // color remapping diff --git a/sonoff/xplg_ws2812.ino b/sonoff/xplg_ws2812.ino index 70eb005fa..39aa3865f 100644 --- a/sonoff/xplg_ws2812.ino +++ b/sonoff/xplg_ws2812.ino @@ -99,11 +99,11 @@ void Ws2812StripShow(void) if (Settings.light_correction) { for (uint16_t i = 0; i < Settings.light_pixels; i++) { c = strip->GetPixelColor(i); - c.R = ledTable[c.R]; - c.G = ledTable[c.G]; - c.B = ledTable[c.B]; + c.R = ledGamma(c.R); + c.G = ledGamma(c.G); + c.B = ledGamma(c.B); #if (USE_WS2812_CTYPE > NEO_3LED) - c.W = ledTable[c.W]; + c.W = ledGamma(c.W); #endif strip->SetPixelColor(i, c); }