]> git.donarmstrong.com Git - qmk_firmware.git/commitdiff
RGB Matrix Overhaul (#5372)
authorXScorpion2 <rcalt2vt@gmail.com>
Wed, 3 Apr 2019 00:24:14 +0000 (19:24 -0500)
committerDrashna Jaelre <drashna@live.com>
Wed, 3 Apr 2019 00:24:14 +0000 (17:24 -0700)
* RGB Matrix overhaul
Breakout of animations to separate files
Integration of optimized int based math lib
Overhaul of rgb_matrix.c and animations for performance

* Updating effect function api for future extensions

* Combined the keypresses || keyreleases define checks into a single define so I stop forgetting it where necessary

* Moving define RGB_MATRIX_KEYREACTIVE_ENABLED earlier in the include chain

37 files changed:
docs/feature_rgb_matrix.md
keyboards/dztech/dz40rgb/config.h
keyboards/dztech/dz60rgb/config.h
keyboards/ergodox_ez/config.h
keyboards/hs60/v1/config.h
layouts/community/ergodox/drashna_glow/config.h
layouts/community/ortho_4x12/drashna/config.h
lib/lib8tion/LICENSE [new file with mode: 0644]
lib/lib8tion/lib8tion.c [new file with mode: 0644]
lib/lib8tion/lib8tion.h [new file with mode: 0644]
lib/lib8tion/math8.h [new file with mode: 0644]
lib/lib8tion/random8.h [new file with mode: 0644]
lib/lib8tion/scale8.h [new file with mode: 0644]
lib/lib8tion/trig8.h [new file with mode: 0644]
quantum/color.c
quantum/quantum.c
quantum/rgb_matrix.c
quantum/rgb_matrix.h
quantum/rgb_matrix_animations/alpha_mods_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/breathing_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/cycle_all_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/cycle_left_right_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/cycle_up_down_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/digital_rain_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/dual_beacon_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/gradient_up_down_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/jellybean_raindrops_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/rainbow_beacon_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/raindrops_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/solid_color_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/solid_reactive_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/solid_reactive_simple_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/solid_splash_anim.h [new file with mode: 0644]
quantum/rgb_matrix_animations/splash_anim.h [new file with mode: 0644]
quantum/rgb_matrix_types.h [new file with mode: 0644]

index 8d1efb12a2750954286a45a80b2660a55e12d522..ec121895459f732edb09db6fab576bf7c8546c19 100644 (file)
@@ -10,39 +10,45 @@ If you want to use single color LED's you should use the [LED Matrix Subsystem](
 
 There is basic support for addressable RGB matrix lighting with the I2C IS31FL3731 RGB controller. To enable it, add this to your `rules.mk`:
 
-    RGB_MATRIX_ENABLE = IS31FL3731
+```C
+RGB_MATRIX_ENABLE = IS31FL3731
+```
 
 Configure the hardware via your `config.h`:
 
-       // This is a 7-bit address, that gets left-shifted and bit 0
-       // set to 0 for write, 1 for read (as per I2C protocol)
-       // The address will vary depending on your wiring:
-       // 0b1110100 AD <-> GND
-       // 0b1110111 AD <-> VCC
-       // 0b1110101 AD <-> SCL
-       // 0b1110110 AD <-> SDA
-       #define DRIVER_ADDR_1 0b1110100
-       #define DRIVER_ADDR_2 0b1110110
-
-       #define DRIVER_COUNT 2
-       #define DRIVER_1_LED_TOTAL 25
-       #define DRIVER_2_LED_TOTAL 24
-       #define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL
+```C
+// This is a 7-bit address, that gets left-shifted and bit 0
+// set to 0 for write, 1 for read (as per I2C protocol)
+// The address will vary depending on your wiring:
+// 0b1110100 AD <-> GND
+// 0b1110111 AD <-> VCC
+// 0b1110101 AD <-> SCL
+// 0b1110110 AD <-> SDA
+#define DRIVER_ADDR_1 0b1110100
+#define DRIVER_ADDR_2 0b1110110
+
+#define DRIVER_COUNT 2
+#define DRIVER_1_LED_TOTAL 25
+#define DRIVER_2_LED_TOTAL 24
+#define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL
+```
 
 Currently only 2 drivers are supported, but it would be trivial to support all 4 combinations.
 
 Define these arrays listing all the LEDs in your `<keyboard>.c`:
 
-       const is31_led g_is31_leds[DRIVER_LED_TOTAL] = {
-       /* Refer to IS31 manual for these locations
-        *   driver
-        *   |  R location
-        *   |  |      G location
-        *   |  |      |      B location
-        *   |  |      |      | */
-           {0, C1_3,  C2_3,  C3_3},
-           ....
-       }
+```C
+const is31_led g_is31_leds[DRIVER_LED_TOTAL] = {
+/* Refer to IS31 manual for these locations
+    *   driver
+    *   |  R location
+    *   |  |      G location
+    *   |  |      |      B location
+    *   |  |      |      | */
+    {0, C1_3,  C2_3,  C3_3},
+    ....
+}
+```
 
 Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](http://www.issi.com/WW/pdf/31FL3731.pdf) and the header file `drivers/issi/is31fl3731.h`. The `driver` is the index of the driver you defined in your `config.h` (`0` or `1` right now).
 
@@ -50,60 +56,70 @@ Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet]
 
 There is basic support for addressable RGB matrix lighting with the I2C IS31FL3733 RGB controller. To enable it, add this to your `rules.mk`:
 
-    RGB_MATRIX_ENABLE = IS31FL3733
+```C
+RGB_MATRIX_ENABLE = IS31FL3733
+```
 
 Configure the hardware via your `config.h`:
 
-       // This is a 7-bit address, that gets left-shifted and bit 0
-       // set to 0 for write, 1 for read (as per I2C protocol)
-       // The address will vary depending on your wiring:
-       // 00 <-> GND
-       // 01 <-> SCL
-       // 10 <-> SDA
-       // 11 <-> VCC
-       // ADDR1 represents A1:A0 of the 7-bit address.
-       // ADDR2 represents A3:A2 of the 7-bit address.
-       // The result is: 0b101(ADDR2)(ADDR1)
-       #define DRIVER_ADDR_1 0b1010000
-       #define DRIVER_ADDR_2 0b1010000 // this is here for compliancy reasons.
-
-       #define DRIVER_COUNT 2
-       #define DRIVER_1_LED_TOTAL 64
-       #define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL
+```C
+// This is a 7-bit address, that gets left-shifted and bit 0
+// set to 0 for write, 1 for read (as per I2C protocol)
+// The address will vary depending on your wiring:
+// 00 <-> GND
+// 01 <-> SCL
+// 10 <-> SDA
+// 11 <-> VCC
+// ADDR1 represents A1:A0 of the 7-bit address.
+// ADDR2 represents A3:A2 of the 7-bit address.
+// The result is: 0b101(ADDR2)(ADDR1)
+#define DRIVER_ADDR_1 0b1010000
+#define DRIVER_ADDR_2 0b1010000 // this is here for compliancy reasons.
+
+#define DRIVER_COUNT 2
+#define DRIVER_1_LED_TOTAL 64
+#define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL
+```
 
 Currently only a single drivers is supported, but it would be trivial to support all 4 combinations. For now define `DRIVER_ADDR_2` as `DRIVER_ADDR_1`
 
 Define these arrays listing all the LEDs in your `<keyboard>.c`:
 
-       const is31_led g_is31_leds[DRIVER_LED_TOTAL] = {
-       /* Refer to IS31 manual for these locations
-        *   driver
-        *   |  R location
-        *   |  |       G location
-        *   |  |       |       B location
-        *   |  |       |       | */
-           {0, B_1,    A_1,    C_1},
-           ....
-       }
+```C
+const is31_led g_is31_leds[DRIVER_LED_TOTAL] = {
+/* Refer to IS31 manual for these locations
+    *   driver
+    *   |  R location
+    *   |  |       G location
+    *   |  |       |       B location
+    *   |  |       |       | */
+    {0, B_1,    A_1,    C_1},
+    ....
+}
+```
 
 Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](http://www.issi.com/WW/pdf/31FL3733.pdf) and the header file `drivers/issi/is31fl3733.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0` right now).
 
 From this point forward the configuration is the same for all the drivers. 
 
-       const rgb_led g_rgb_leds[DRIVER_LED_TOTAL] = {
-       /* {row | col << 4}
-        *    |           {x=0..224, y=0..64}
-        *    |              |               modifier
-        *    |              |                 | */
-           {{0|(0<<4)},   {20.36*0, 21.33*0}, 1},
-           {{0|(1<<4)},   {20.36*1, 21.33*0}, 1},
-           ....
-       }
+```C
+const rgb_led g_rgb_leds[DRIVER_LED_TOTAL] = {
+/*  {row | col << 4}
+    *    |         {x=0..224, y=0..64}
+    *    |            |              modifier
+    *    |            |                | */
+    {{0|(0<<4)},   {20.36*0, 21.33*0}, 1},
+    {{0|(1<<4)},   {20.36*1, 21.33*0}, 1},
+    ....
+}
+```
 
 The format for the matrix position used in this array is `{row | (col << 4)}`. The `x` is between (inclusive) 0-224, and `y` is between (inclusive) 0-64. The easiest way to calculate these positions is:
 
-    x = 224 / ( NUMBER_OF_COLS - 1 ) * ROW_POSITION
-    y = 64 / (NUMBER_OF_ROWS - 1 ) * COL_POSITION
+```C
+x = 224 / ( NUMBER_OF_COLS - 1 ) * ROW_POSITION
+y = 64 / (NUMBER_OF_ROWS - 1 ) * COL_POSITION
+```
 
 Where all variables are decimels/floats.
 
@@ -113,48 +129,50 @@ Where all variables are decimels/floats.
 
 All RGB keycodes are currently shared with the RGBLIGHT system:
 
-       * `RGB_TOG` - toggle
-       * `RGB_MOD` - cycle through modes
-       * `RGB_HUI` - increase hue
-       * `RGB_HUD` - decrease hue
-       * `RGB_SAI` - increase saturation
-       * `RGB_SAD` - decrease saturation
-       * `RGB_VAI` - increase value
-       * `RGB_VAD` - decrease value
-       * `RGB_SPI` - increase speed effect (no EEPROM support)
-       * `RGB_SPD` - decrease speed effect (no EEPROM support)
-
-
-       * `RGB_MODE_*` keycodes will generally work, but are not currently mapped to the correct effects for the RGB Matrix system
+* `RGB_TOG` - toggle
+* `RGB_MOD` - cycle through modes
+* `RGB_HUI` - increase hue
+* `RGB_HUD` - decrease hue
+* `RGB_SAI` - increase saturation
+* `RGB_SAD` - decrease saturation
+* `RGB_VAI` - increase value
+* `RGB_VAD` - decrease value
+* `RGB_SPI` - increase speed effect (no EEPROM support)
+* `RGB_SPD` - decrease speed effect (no EEPROM support)
+* `RGB_MODE_*` keycodes will generally work, but are not currently mapped to the correct effects for the RGB Matrix system
 
 ## RGB Matrix Effects
 
-These are the effects that are currently available:
-
-       enum rgb_matrix_effects {
-           RGB_MATRIX_SOLID_COLOR = 1,
-           RGB_MATRIX_ALPHAS_MODS,
-           RGB_MATRIX_DUAL_BEACON,
-           RGB_MATRIX_GRADIENT_UP_DOWN,
-           RGB_MATRIX_RAINDROPS,
-           RGB_MATRIX_CYCLE_ALL,
-           RGB_MATRIX_CYCLE_LEFT_RIGHT,
-           RGB_MATRIX_CYCLE_UP_DOWN,
-           RGB_MATRIX_RAINBOW_BEACON,
-           RGB_MATRIX_RAINBOW_PINWHEELS,
-           RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
-           RGB_MATRIX_JELLYBEAN_RAINDROPS,
-           RGB_MATRIX_DIGITAL_RAIN,
-       #ifdef RGB_MATRIX_KEYPRESSES
-           RGB_MATRIX_SOLID_REACTIVE,
-           RGB_MATRIX_REACTIVE_SIMPLE,
-           RGB_MATRIX_SPLASH,
-           RGB_MATRIX_MULTISPLASH,
-           RGB_MATRIX_SOLID_SPLASH,
-           RGB_MATRIX_SOLID_MULTISPLASH,
-       #endif
-           RGB_MATRIX_EFFECT_MAX
-       };
+All effects have been configured to support current configuration values (Hue, Saturation, Value, & Speed) unless otherwise noted below. These are the effects that are currently available:
+
+```C
+enum rgb_matrix_effects {
+    RGB_MATRIX_NONE = 0,
+    RGB_MATRIX_SOLID_COLOR = 1,     // Static single hue, no speed support
+    RGB_MATRIX_ALPHAS_MODS,         // Static dual hue, speed is hue for secondary hue
+    RGB_MATRIX_GRADIENT_UP_DOWN,    // Static gradient top to bottom, speed controls how much gradient changes
+    RGB_MATRIX_BREATHING,           // Single hue brightness cycling animation
+    RGB_MATRIX_CYCLE_ALL,           // Full keyboard solid hue cycling through full gradient
+    RGB_MATRIX_CYCLE_LEFT_RIGHT,    // Full gradient scrolling left to right 
+    RGB_MATRIX_CYCLE_UP_DOWN,       // Full gradient scrolling top to bottom
+    RGB_MATRIX_RAINBOW_MOVING_CHEVRON,  // Full gradent Chevron shapped scrolling left to right
+    RGB_MATRIX_DUAL_BEACON,         // Full gradient spinning around center of keyboard
+    RGB_MATRIX_RAINBOW_BEACON,      // Full tighter gradient spinning around center of keyboard
+    RGB_MATRIX_RAINBOW_PINWHEELS,   // Full dual gradients spinning two halfs of keyboard
+    RGB_MATRIX_RAINDROPS,           // Randomly changes a single key's hue
+    RGB_MATRIX_JELLYBEAN_RAINDROPS, // Randomly changes a single key's hue and saturation
+    RGB_MATRIX_DIGITAL_RAIN,        // That famous computer simulation
+#if defined(RGB_MATRIX_KEYPRESSES) || defined(RGB_MATRIX_KEYRELEASES)
+    RGB_MATRIX_SOLID_REACTIVE_SIMPLE,   // Pulses keys hit to hue & value then fades value out
+    RGB_MATRIX_SOLID_REACTIVE,      // Static single hue, pulses keys hit to shifted hue then fades to current hue
+    RGB_MATRIX_SPLASH,              // Full gradient & value pulse away from a single key hit then fades value out
+    RGB_MATRIX_MULTISPLASH,         // Full gradient & value pulse away from multiple key hits then fades value out
+    RGB_MATRIX_SOLID_SPLASH,        // Hue & value pulse away from a single key hit then fades value out
+    RGB_MATRIX_SOLID_MULTISPLASH,   // Hue & value pulse away from multiple key hits then fades value out
+#endif
+    RGB_MATRIX_EFFECT_MAX
+};
+```
     
 You can disable a single effect by defining `DISABLE_[EFFECT_NAME]` in your `config.h`:
 
@@ -162,19 +180,20 @@ You can disable a single effect by defining `DISABLE_[EFFECT_NAME]` in your `con
 |Define                                             |Description                                 |
 |---------------------------------------------------|--------------------------------------------|
 |`#define DISABLE_RGB_MATRIX_ALPHAS_MODS`           |Disables `RGB_MATRIX_ALPHAS_MODS`           |
-|`#define DISABLE_RGB_MATRIX_DUAL_BEACON`           |Disables `RGB_MATRIX_DUAL_BEACON`           |
 |`#define DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN`      |Disables `RGB_MATRIX_GRADIENT_UP_DOWN`      |
-|`#define DISABLE_RGB_MATRIX_RAINDROPS`             |Disables `RGB_MATRIX_RAINDROPS`             |
+|`#define DISABLE_RGB_MATRIX_BREATHING`             |Disables `RGB_MATRIX_BREATHING`             |
 |`#define DISABLE_RGB_MATRIX_CYCLE_ALL`             |Disables `RGB_MATRIX_CYCLE_ALL`             |
 |`#define DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT`      |Disables `RGB_MATRIX_CYCLE_LEFT_RIGHT`      |
 |`#define DISABLE_RGB_MATRIX_CYCLE_UP_DOWN`         |Disables `RGB_MATRIX_CYCLE_UP_DOWN`         |
+|`#define DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON`|Disables `RGB_MATRIX_RAINBOW_MOVING_CHEVRON`|
+|`#define DISABLE_RGB_MATRIX_DUAL_BEACON`           |Disables `RGB_MATRIX_DUAL_BEACON`           |
 |`#define DISABLE_RGB_MATRIX_RAINBOW_BEACON`        |Disables `RGB_MATRIX_RAINBOW_BEACON`        |
 |`#define DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS`     |Disables `RGB_MATRIX_RAINBOW_PINWHEELS`     |
-|`#define DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON`|Disables `RGB_MATRIX_RAINBOW_MOVING_CHEVRON`|
+|`#define DISABLE_RGB_MATRIX_RAINDROPS`             |Disables `RGB_MATRIX_RAINDROPS`             |
 |`#define DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS`   |Disables `RGB_MATRIX_JELLYBEAN_RAINDROPS`   |
 |`#define DISABLE_RGB_MATRIX_DIGITAL_RAIN`          |Disables `RGB_MATRIX_DIGITAL_RAIN`          |
 |`#define DISABLE_RGB_MATRIX_SOLID_REACTIVE`        |Disables `RGB_MATRIX_SOLID_REACTIVE`        |
-|`#define DISABLE_RGB_MATRIX_REACTIVE_SIMPLE`       |Disables `RGB_MATRIX_REACTIVE_SIMPLE`       |
+|`#define DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE` |Disables `RGB_MATRIX_SOLID_REACTIVE_SIMPLEE`|
 |`#define DISABLE_RGB_MATRIX_SPLASH`                |Disables `RGB_MATRIX_SPLASH`                |
 |`#define DISABLE_RGB_MATRIX_MULTISPLASH`           |Disables `RGB_MATRIX_MULTISPLASH`           |
 |`#define DISABLE_RGB_MATRIX_SOLID_SPLASH`          |Disables `RGB_MATRIX_SOLID_SPLASH`          |
@@ -185,26 +204,33 @@ You can disable a single effect by defining `DISABLE_[EFFECT_NAME]` in your `con
 
 Custom layer effects can be done by defining this in your `<keyboard>.c`:
 
-    void rgb_matrix_indicators_kb(void) {
-        rgb_matrix_set_color(index, red, green, blue);
-    }
+```C
+void rgb_matrix_indicators_kb(void) {
+    rgb_matrix_set_color(index, red, green, blue);
+}
+```
 
 A similar function works in the keymap as `rgb_matrix_indicators_user`.
 
 ## Additional `config.h` Options
 
-       #define RGB_MATRIX_KEYPRESSES // reacts to keypresses (will slow down matrix scan by a lot)
-       #define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (not recommened)
-       #define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
-       #define RGB_DISABLE_WHEN_USB_SUSPENDED false // turn off effects when suspended
-    #define RGB_MATRIX_SKIP_FRAMES 1 // number of frames to skip when displaying animations (0 is full effect) if not defined defaults to 1
-    #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
+```C
+#define RGB_MATRIX_KEYPRESSES // reacts to keypresses
+#define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses)
+#define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
+#define RGB_DISABLE_WHEN_USB_SUSPENDED false // turn off effects when suspended
+#define RGB_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
+#define RGB_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness)
+#define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
+```
 
 ## EEPROM storage
 
 The EEPROM for it is currently shared with the RGBLIGHT system (it's generally assumed only one RGB would be used at a time), but could be configured to use its own 32bit address with:
 
-    #define EECONFIG_RGB_MATRIX (uint32_t *)16
+```C
+#define EECONFIG_RGB_MATRIX (uint32_t *)16
+```
 
 Where `16` is an unused index from `eeconfig.h`.
 
@@ -212,12 +238,14 @@ Where `16` is an unused index from `eeconfig.h`.
 
 To use the suspend feature, add this to your `<keyboard>.c`:
 
-       void suspend_power_down_kb(void)
-       {
-           rgb_matrix_set_suspend_state(true);
-       }
-
-       void suspend_wakeup_init_kb(void)
-       {
-           rgb_matrix_set_suspend_state(false);
-       }
+```C
+void suspend_power_down_kb(void)
+{
+    rgb_matrix_set_suspend_state(true);
+}
+
+void suspend_wakeup_init_kb(void)
+{
+    rgb_matrix_set_suspend_state(false);
+}
+```
index 5b9f6dc08992c9556b5ad48675a59d90b4ba37d2..0d6c4ae39a748f37961d3c1369187a7c20f194c0 100644 (file)
@@ -22,7 +22,6 @@
 #define DEBOUNCE 3
 #define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
 #define RGB_DISABLE_WHEN_USB_SUSPENDED false // turn off effects when suspended
-#define RGB_MATRIX_SKIP_FRAMES 10
 #define RGB_MATRIX_KEYPRESSES
 #define DISABLE_RGB_MATRIX_SPLASH
 #define DISABLE_RGB_MATRIX_MULTISPLASH
index 19f32440eaacbf014aeb0ea1492f3173da5ec4cd..3a6f4bcc07ee6e2a89d5420408b50794321ce007 100644 (file)
@@ -23,7 +23,6 @@
 #define DEBOUNCE 3
 #define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
 #define RGB_DISABLE_WHEN_USB_SUSPENDED false // turn off effects when suspended
-#define RGB_MATRIX_SKIP_FRAMES 10
 #define RGB_MATRIX_KEYPRESSES
 #define DISABLE_RGB_MATRIX_SPLASH
 #define DISABLE_RGB_MATRIX_MULTISPLASH
index 096368f7abce5da65b8a8a05b8221b1c7247c79e..a75edd415440d1c408ee3a8bc505971617479708 100644 (file)
@@ -109,7 +109,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define DRIVER_1_LED_TOTAL 24
 #define DRIVER_2_LED_TOTAL 24
 #define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL
-#define RGB_MATRIX_SKIP_FRAMES 10
 
 // #define RGBLIGHT_COLOR_LAYER_0 0x00, 0x00, 0xFF
 /* #define RGBLIGHT_COLOR_LAYER_1 0x00, 0x00, 0xFF */
index 7581e54b61956b9308ab56ffc33013232b2cb3ec..528f08bb9948375cc375eeac052397ad50e19b49 100644 (file)
@@ -120,7 +120,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
 #define RGB_DISABLE_WHEN_USB_SUSPENDED false // turn off effects when suspended
-#define RGB_MATRIX_SKIP_FRAMES 0
 #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 215
 
 #define DRIVER_ADDR_1 0b1110100
index cdca5ba4b528c807966ad16c3473e2ac27a38673..a10c4bb297c71d9a7125f96c31f844817e533dc3 100644 (file)
@@ -13,7 +13,6 @@
 // #define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (not recommened)
 // #define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
 #define RGB_DISABLE_WHEN_USB_SUSPENDED  true// turn off effects when suspended
-// #define RGB_MATRIX_SKIP_FRAMES 1 // number of frames to skip when displaying animations (0 is full effect) if not defined defaults to 1
 // #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
 // #define EECONFIG_RGB_MATRIX (uint32_t *)16
 #endif
index f648d112790612f79ad08776237bfcf10f525272..1858ee3a4f5dc5a81364cc78343ab62f29fba712 100644 (file)
@@ -18,7 +18,6 @@
 // #define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (not recommened)
 // #define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
 #define RGB_DISABLE_WHEN_USB_SUSPENDED true// turn off effects when suspended
-// #define RGB_MATRIX_SKIP_FRAMES 1 // number of frames to skip when displaying animations (0 is full effect) if not defined defaults to 1
 // #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
 #define EECONFIG_RGB_MATRIX (uint32_t *)15
 #endif
diff --git a/lib/lib8tion/LICENSE b/lib/lib8tion/LICENSE
new file mode 100644 (file)
index 0000000..ebe4763
--- /dev/null
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 FastLED
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/lib8tion/lib8tion.c b/lib/lib8tion/lib8tion.c
new file mode 100644 (file)
index 0000000..84b3e9c
--- /dev/null
@@ -0,0 +1,242 @@
+#define FASTLED_INTERNAL
+#include <stdint.h>
+
+#define RAND16_SEED  1337
+uint16_t rand16seed = RAND16_SEED;
+
+
+// memset8, memcpy8, memmove8:
+//  optimized avr replacements for the standard "C" library
+//  routines memset, memcpy, and memmove.
+//
+//  There are two techniques that make these routines
+//  faster than the standard avr-libc routines.
+//  First, the loops are unrolled 2X, meaning that
+//  the average loop overhead is cut in half.
+//  And second, the compare-and-branch at the bottom
+//  of each loop decrements the low byte of the
+//  counter, and if the carry is clear, it branches
+//  back up immediately.  Only if the low byte math
+//  causes carry do we bother to decrement the high
+//  byte and check that result for carry as well.
+//  Results for a 100-byte buffer are 20-40% faster
+//  than standard avr-libc, at a cost of a few extra
+//  bytes of code.
+
+#if defined(__AVR__)
+//__attribute__ ((noinline))
+void * memset8 ( void * ptr, uint8_t val, uint16_t num )
+{
+    asm volatile(
+         "  movw r26, %[ptr]        \n\t"
+         "  sbrs %A[num], 0         \n\t"
+         "  rjmp Lseteven_%=        \n\t"
+         "  rjmp Lsetodd_%=         \n\t"
+         "Lsetloop_%=:              \n\t"
+         "  st X+, %[val]           \n\t"
+         "Lsetodd_%=:               \n\t"
+         "  st X+, %[val]           \n\t"
+         "Lseteven_%=:              \n\t"
+         "  subi %A[num], 2         \n\t"
+         "  brcc Lsetloop_%=        \n\t"
+         "  sbci %B[num], 0         \n\t"
+         "  brcc Lsetloop_%=        \n\t"
+         : [num] "+r" (num)
+         : [ptr]  "r" (ptr),
+           [val]  "r" (val)
+         : "memory"
+         );
+    return ptr;
+}
+
+
+
+//__attribute__ ((noinline))
+void * memcpy8 ( void * dst, const void* src, uint16_t num )
+{
+    asm volatile(
+         "  movw r30, %[src]        \n\t"
+         "  movw r26, %[dst]        \n\t"
+         "  sbrs %A[num], 0         \n\t"
+         "  rjmp Lcpyeven_%=        \n\t"
+         "  rjmp Lcpyodd_%=         \n\t"
+         "Lcpyloop_%=:              \n\t"
+         "  ld __tmp_reg__, Z+      \n\t"
+         "  st X+, __tmp_reg__      \n\t"
+         "Lcpyodd_%=:               \n\t"
+         "  ld __tmp_reg__, Z+      \n\t"
+         "  st X+, __tmp_reg__      \n\t"
+         "Lcpyeven_%=:              \n\t"
+         "  subi %A[num], 2         \n\t"
+         "  brcc Lcpyloop_%=        \n\t"
+         "  sbci %B[num], 0         \n\t"
+         "  brcc Lcpyloop_%=        \n\t"
+         : [num] "+r" (num)
+         : [src] "r" (src),
+           [dst] "r" (dst)
+         : "memory"
+         );
+    return dst;
+}
+
+//__attribute__ ((noinline))
+void * memmove8 ( void * dst, const void* src, uint16_t num )
+{
+    if( src > dst) {
+        // if src > dst then we can use the forward-stepping memcpy8
+        return memcpy8( dst, src, num);
+    } else {
+        // if src < dst then we have to step backward:
+        dst = (char*)dst + num;
+        src = (char*)src + num;
+        asm volatile(
+             "  movw r30, %[src]        \n\t"
+             "  movw r26, %[dst]        \n\t"
+             "  sbrs %A[num], 0         \n\t"
+             "  rjmp Lmoveven_%=        \n\t"
+             "  rjmp Lmovodd_%=         \n\t"
+             "Lmovloop_%=:              \n\t"
+             "  ld __tmp_reg__, -Z      \n\t"
+             "  st -X, __tmp_reg__      \n\t"
+             "Lmovodd_%=:               \n\t"
+             "  ld __tmp_reg__, -Z      \n\t"
+             "  st -X, __tmp_reg__      \n\t"
+             "Lmoveven_%=:              \n\t"
+             "  subi %A[num], 2         \n\t"
+             "  brcc Lmovloop_%=        \n\t"
+             "  sbci %B[num], 0         \n\t"
+             "  brcc Lmovloop_%=        \n\t"
+             : [num] "+r" (num)
+             : [src] "r" (src),
+               [dst] "r" (dst)
+             : "memory"
+             );
+        return dst;
+    }
+}
+
+#endif /* AVR */
+
+
+
+
+#if 0
+// TEST / VERIFICATION CODE ONLY BELOW THIS POINT
+#include <Arduino.h>
+#include "lib8tion.h"
+
+void test1abs( int8_t i)
+{
+    Serial.print("abs("); Serial.print(i); Serial.print(") = ");
+    int8_t j = abs8(i);
+    Serial.print(j); Serial.println(" ");
+}
+
+void testabs()
+{
+    delay(5000);
+    for( int8_t q = -128; q != 127; q++) {
+        test1abs(q);
+    }
+    for(;;){};
+}
+
+
+void testmul8()
+{
+    delay(5000);
+    byte r, c;
+
+    Serial.println("mul8:");
+    for( r = 0; r <= 20; r += 1) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 20; c += 1) {
+            byte t;
+            t = mul8( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+    Serial.println("done.");
+    for(;;){};
+}
+
+
+void testscale8()
+{
+    delay(5000);
+    byte r, c;
+
+    Serial.println("scale8:");
+    for( r = 0; r <= 240; r += 10) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 240; c += 10) {
+            byte t;
+            t = scale8( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+
+    Serial.println(' ');
+    Serial.println("scale8_video:");
+
+    for( r = 0; r <= 100; r += 4) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 100; c += 4) {
+            byte t;
+            t = scale8_video( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+
+    Serial.println("done.");
+    for(;;){};
+}
+
+
+
+void testqadd8()
+{
+    delay(5000);
+    byte r, c;
+    for( r = 0; r <= 240; r += 10) {
+        Serial.print(r); Serial.print(" : ");
+        for( c = 0; c <= 240; c += 10) {
+            byte t;
+            t = qadd8( r, c);
+            Serial.print(t); Serial.print(' ');
+        }
+        Serial.println(' ');
+    }
+    Serial.println("done.");
+    for(;;){};
+}
+
+void testnscale8x3()
+{
+    delay(5000);
+    byte r, g, b, sc;
+    for( byte z = 0; z < 10; z++) {
+        r = random8(); g = random8(); b = random8(); sc = random8();
+
+        Serial.print("nscale8x3_video( ");
+        Serial.print(r); Serial.print(", ");
+        Serial.print(g); Serial.print(", ");
+        Serial.print(b); Serial.print(", ");
+        Serial.print(sc); Serial.print(") = [ ");
+
+        nscale8x3_video( r, g, b, sc);
+
+        Serial.print(r); Serial.print(", ");
+        Serial.print(g); Serial.print(", ");
+        Serial.print(b); Serial.print("]");
+
+        Serial.println(' ');
+    }
+    Serial.println("done.");
+    for(;;){};
+}
+
+#endif
diff --git a/lib/lib8tion/lib8tion.h b/lib/lib8tion/lib8tion.h
new file mode 100644 (file)
index 0000000..d93c748
--- /dev/null
@@ -0,0 +1,934 @@
+#ifndef __INC_LIB8TION_H
+#define __INC_LIB8TION_H
+
+/*
+
+ Fast, efficient 8-bit math functions specifically
+ designed for high-performance LED programming.
+
+ Because of the AVR(Arduino) and ARM assembly language
+ implementations provided, using these functions often
+ results in smaller and faster code than the equivalent
+ program using plain "C" arithmetic and logic.
+
+
+ Included are:
+
+
+ - Saturating unsigned 8-bit add and subtract.
+   Instead of wrapping around if an overflow occurs,
+   these routines just 'clamp' the output at a maxumum
+   of 255, or a minimum of 0.  Useful for adding pixel
+   values.  E.g., qadd8( 200, 100) = 255.
+
+     qadd8( i, j) == MIN( (i + j), 0xFF )
+     qsub8( i, j) == MAX( (i - j), 0 )
+
+ - Saturating signed 8-bit ("7-bit") add.
+     qadd7( i, j) == MIN( (i + j), 0x7F)
+
+
+ - Scaling (down) of unsigned 8- and 16- bit values.
+   Scaledown value is specified in 1/256ths.
+     scale8( i, sc) == (i * sc) / 256
+     scale16by8( i, sc) == (i * sc) / 256
+
+   Example: scaling a 0-255 value down into a
+   range from 0-99:
+     downscaled = scale8( originalnumber, 100);
+
+   A special version of scale8 is provided for scaling
+   LED brightness values, to make sure that they don't
+   accidentally scale down to total black at low
+   dimming levels, since that would look wrong:
+     scale8_video( i, sc) = ((i * sc) / 256) +? 1
+
+   Example: reducing an LED brightness by a
+   dimming factor:
+     new_bright = scale8_video( orig_bright, dimming);
+
+
+ - Fast 8- and 16- bit unsigned random numbers.
+   Significantly faster than Arduino random(), but
+   also somewhat less random.  You can add entropy.
+     random8()       == random from 0..255
+     random8( n)     == random from 0..(N-1)
+     random8( n, m)  == random from N..(M-1)
+
+     random16()      == random from 0..65535
+     random16( n)    == random from 0..(N-1)
+     random16( n, m) == random from N..(M-1)
+
+     random16_set_seed( k)    ==  seed = k
+     random16_add_entropy( k) ==  seed += k
+
+
+ - Absolute value of a signed 8-bit value.
+     abs8( i)     == abs( i)
+
+
+ - 8-bit math operations which return 8-bit values.
+   These are provided mostly for completeness,
+   not particularly for performance.
+     mul8( i, j)  == (i * j) & 0xFF
+     add8( i, j)  == (i + j) & 0xFF
+     sub8( i, j)  == (i - j) & 0xFF
+
+
+ - Fast 16-bit approximations of sin and cos.
+   Input angle is a uint16_t from 0-65535.
+   Output is a signed int16_t from -32767 to 32767.
+      sin16( x)  == sin( (x/32768.0) * pi) * 32767
+      cos16( x)  == cos( (x/32768.0) * pi) * 32767
+   Accurate to more than 99% in all cases.
+
+ - Fast 8-bit approximations of sin and cos.
+   Input angle is a uint8_t from 0-255.
+   Output is an UNsigned uint8_t from 0 to 255.
+       sin8( x)  == (sin( (x/128.0) * pi) * 128) + 128
+       cos8( x)  == (cos( (x/128.0) * pi) * 128) + 128
+   Accurate to within about 2%.
+
+
+ - Fast 8-bit "easing in/out" function.
+     ease8InOutCubic(x) == 3(x^i) - 2(x^3)
+     ease8InOutApprox(x) ==
+       faster, rougher, approximation of cubic easing
+     ease8InOutQuad(x) == quadratic (vs cubic) easing
+
+ - Cubic, Quadratic, and Triangle wave functions.
+   Input is a uint8_t representing phase withing the wave,
+     similar to how sin8 takes an angle 'theta'.
+   Output is a uint8_t representing the amplitude of
+     the wave at that point.
+       cubicwave8( x)
+       quadwave8( x)
+       triwave8( x)
+
+ - Square root for 16-bit integers.  About three times
+   faster and five times smaller than Arduino's built-in
+   generic 32-bit sqrt routine.
+     sqrt16( uint16_t x ) == sqrt( x)
+
+ - Dimming and brightening functions for 8-bit
+   light values.
+     dim8_video( x)  == scale8_video( x, x)
+     dim8_raw( x)    == scale8( x, x)
+     dim8_lin( x)    == (x<128) ? ((x+1)/2) : scale8(x,x)
+     brighten8_video( x) == 255 - dim8_video( 255 - x)
+     brighten8_raw( x) == 255 - dim8_raw( 255 - x)
+     brighten8_lin( x) == 255 - dim8_lin( 255 - x)
+   The dimming functions in particular are suitable
+   for making LED light output appear more 'linear'.
+
+
+ - Linear interpolation between two values, with the
+   fraction between them expressed as an 8- or 16-bit
+   fixed point fraction (fract8 or fract16).
+     lerp8by8(   fromU8, toU8, fract8 )
+     lerp16by8(  fromU16, toU16, fract8 )
+     lerp15by8(  fromS16, toS16, fract8 )
+       == from + (( to - from ) * fract8) / 256)
+     lerp16by16( fromU16, toU16, fract16 )
+       == from + (( to - from ) * fract16) / 65536)
+     map8( in, rangeStart, rangeEnd)
+       == map( in, 0, 255, rangeStart, rangeEnd);
+
+ - Optimized memmove, memcpy, and memset, that are
+   faster than standard avr-libc 1.8.
+      memmove8( dest, src,  bytecount)
+      memcpy8(  dest, src,  bytecount)
+      memset8(  buf, value, bytecount)
+
+ - Beat generators which return sine or sawtooth
+   waves in a specified number of Beats Per Minute.
+   Sine wave beat generators can specify a low and
+   high range for the output.  Sawtooth wave beat
+   generators always range 0-255 or 0-65535.
+     beatsin8( BPM, low8, high8)
+         = (sine(beatphase) * (high8-low8)) + low8
+     beatsin16( BPM, low16, high16)
+         = (sine(beatphase) * (high16-low16)) + low16
+     beatsin88( BPM88, low16, high16)
+         = (sine(beatphase) * (high16-low16)) + low16
+     beat8( BPM)  = 8-bit repeating sawtooth wave
+     beat16( BPM) = 16-bit repeating sawtooth wave
+     beat88( BPM88) = 16-bit repeating sawtooth wave
+   BPM is beats per minute in either simple form
+   e.g. 120, or Q8.8 fixed-point form.
+   BPM88 is beats per minute in ONLY Q8.8 fixed-point
+   form.
+
+Lib8tion is pronounced like 'libation': lie-BAY-shun
+
+*/
+
+
+
+#include <stdint.h>
+
+#define LIB8STATIC __attribute__ ((unused)) static inline
+#define LIB8STATIC_ALWAYS_INLINE __attribute__ ((always_inline)) static inline
+
+#if !defined(__AVR__)
+#include <string.h>
+// for memmove, memcpy, and memset if not defined here
+#endif
+
+#if defined(__arm__)
+
+#if defined(FASTLED_TEENSY3)
+// Can use Cortex M4 DSP instructions
+#define QADD8_C 0
+#define QADD7_C 0
+#define QADD8_ARM_DSP_ASM 1
+#define QADD7_ARM_DSP_ASM 1
+#else
+// Generic ARM
+#define QADD8_C 1
+#define QADD7_C 1
+#endif
+
+#define QSUB8_C 1
+#define SCALE8_C 1
+#define SCALE16BY8_C 1
+#define SCALE16_C 1
+#define ABS8_C 1
+#define MUL8_C 1
+#define QMUL8_C 1
+#define ADD8_C 1
+#define SUB8_C 1
+#define EASE8_C 1
+#define AVG8_C 1
+#define AVG7_C 1
+#define AVG16_C 1
+#define AVG15_C 1
+#define BLEND8_C 1
+
+
+#elif defined(__AVR__)
+
+// AVR ATmega and friends Arduino
+
+#define QADD8_C 0
+#define QADD7_C 0
+#define QSUB8_C 0
+#define ABS8_C 0
+#define ADD8_C 0
+#define SUB8_C 0
+#define AVG8_C 0
+#define AVG7_C 0
+#define AVG16_C 0
+#define AVG15_C 0
+
+#define QADD8_AVRASM 1
+#define QADD7_AVRASM 1
+#define QSUB8_AVRASM 1
+#define ABS8_AVRASM 1
+#define ADD8_AVRASM 1
+#define SUB8_AVRASM 1
+#define AVG8_AVRASM 1
+#define AVG7_AVRASM 1
+#define AVG16_AVRASM 1
+#define AVG15_AVRASM 1
+
+// Note: these require hardware MUL instruction
+//       -- sorry, ATtiny!
+#if !defined(LIB8_ATTINY)
+#define SCALE8_C 0
+#define SCALE16BY8_C 0
+#define SCALE16_C 0
+#define MUL8_C 0
+#define QMUL8_C 0
+#define EASE8_C 0
+#define BLEND8_C 0
+#define SCALE8_AVRASM 1
+#define SCALE16BY8_AVRASM 1
+#define SCALE16_AVRASM 1
+#define MUL8_AVRASM 1
+#define QMUL8_AVRASM 1
+#define EASE8_AVRASM 1
+#define CLEANUP_R1_AVRASM 1
+#define BLEND8_AVRASM 1
+#else
+// On ATtiny, we just use C implementations
+#define SCALE8_C 1
+#define SCALE16BY8_C 1
+#define SCALE16_C 1
+#define MUL8_C 1
+#define QMUL8_C 1
+#define EASE8_C 1
+#define BLEND8_C 1
+#define SCALE8_AVRASM 0
+#define SCALE16BY8_AVRASM 0
+#define SCALE16_AVRASM 0
+#define MUL8_AVRASM 0
+#define QMUL8_AVRASM 0
+#define EASE8_AVRASM 0
+#define BLEND8_AVRASM 0
+#endif
+
+#else
+
+// unspecified architecture, so
+// no ASM, everything in C
+#define QADD8_C 1
+#define QADD7_C 1
+#define QSUB8_C 1
+#define SCALE8_C 1
+#define SCALE16BY8_C 1
+#define SCALE16_C 1
+#define ABS8_C 1
+#define MUL8_C 1
+#define QMUL8_C 1
+#define ADD8_C 1
+#define SUB8_C 1
+#define EASE8_C 1
+#define AVG8_C 1
+#define AVG7_C 1
+#define AVG16_C 1
+#define AVG15_C 1
+#define BLEND8_C 1
+
+#endif
+
+///@defgroup lib8tion Fast math functions
+///A variety of functions for working with numbers.
+///@{
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// typdefs for fixed-point fractional types.
+//
+// sfract7 should be interpreted as signed 128ths.
+// fract8 should be interpreted as unsigned 256ths.
+// sfract15 should be interpreted as signed 32768ths.
+// fract16 should be interpreted as unsigned 65536ths.
+//
+// Example: if a fract8 has the value "64", that should be interpreted
+//          as 64/256ths, or one-quarter.
+//
+//
+//  fract8   range is 0 to 0.99609375
+//                 in steps of 0.00390625
+//
+//  sfract7  range is -0.9921875 to 0.9921875
+//                 in steps of 0.0078125
+//
+//  fract16  range is 0 to 0.99998474121
+//                 in steps of 0.00001525878
+//
+//  sfract15 range is -0.99996948242 to 0.99996948242
+//                 in steps of 0.00003051757
+//
+
+/// ANSI unsigned short _Fract.  range is 0 to 0.99609375
+///                 in steps of 0.00390625
+typedef uint8_t   fract8;   ///< ANSI: unsigned short _Fract
+
+///  ANSI: signed short _Fract.  range is -0.9921875 to 0.9921875
+///                 in steps of 0.0078125
+typedef int8_t    sfract7;  ///< ANSI: signed   short _Fract
+
+///  ANSI: unsigned _Fract.  range is 0 to 0.99998474121
+///                 in steps of 0.00001525878
+typedef uint16_t  fract16;  ///< ANSI: unsigned       _Fract
+
+///  ANSI: signed _Fract.  range is -0.99996948242 to 0.99996948242
+///                 in steps of 0.00003051757
+typedef int16_t   sfract15; ///< ANSI: signed         _Fract
+
+
+// accumXY types should be interpreted as X bits of integer,
+//         and Y bits of fraction.
+//         E.g., accum88 has 8 bits of int, 8 bits of fraction
+
+typedef uint16_t  accum88;  ///< ANSI: unsigned short _Accum.  8 bits int, 8 bits fraction
+typedef int16_t   saccum78; ///< ANSI: signed   short _Accum.  7 bits int, 8 bits fraction
+typedef uint32_t  accum1616;///< ANSI: signed         _Accum. 16 bits int, 16 bits fraction
+typedef int32_t   saccum1516;///< ANSI: signed         _Accum. 15 bits int, 16 bits fraction
+typedef uint16_t  accum124; ///< no direct ANSI counterpart. 12 bits int, 4 bits fraction
+typedef int32_t   saccum114;///< no direct ANSI counterpart. 1 bit int, 14 bits fraction
+
+
+
+#include "math8.h"
+#include "scale8.h"
+#include "random8.h"
+#include "trig8.h"
+
+///////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// float-to-fixed and fixed-to-float conversions
+//
+// Note that anything involving a 'float' on AVR will be slower.
+
+/// sfract15ToFloat: conversion from sfract15 fixed point to
+///                  IEEE754 32-bit float.
+LIB8STATIC float sfract15ToFloat( sfract15 y)
+{
+    return y / 32768.0;
+}
+
+/// conversion from IEEE754 float in the range (-1,1)
+///                  to 16-bit fixed point.  Note that the extremes of
+///                  one and negative one are NOT representable.  The
+///                  representable range is basically
+LIB8STATIC sfract15 floatToSfract15( float f)
+{
+    return f * 32768.0;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// memmove8, memcpy8, and memset8:
+//   alternatives to memmove, memcpy, and memset that are
+//   faster on AVR than standard avr-libc 1.8
+
+#if defined(__AVR__)
+void * memmove8( void * dst, const void * src, uint16_t num );
+void * memcpy8 ( void * dst, const void * src, uint16_t num )  __attribute__ ((noinline));
+void * memset8 ( void * ptr, uint8_t value, uint16_t num ) __attribute__ ((noinline)) ;
+#else
+// on non-AVR platforms, these names just call standard libc.
+#define memmove8 memmove
+#define memcpy8 memcpy
+#define memset8 memset
+#endif
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// linear interpolation, such as could be used for Perlin noise, etc.
+//
+
+// A note on the structure of the lerp functions:
+// The cases for b>a and b<=a are handled separately for
+// speed: without knowing the relative order of a and b,
+// the value (a-b) might be overflow the width of a or b,
+// and have to be promoted to a wider, slower type.
+// To avoid that, we separate the two cases, and are able
+// to do all the math in the same width as the arguments,
+// which is much faster and smaller on AVR.
+
+/// linear interpolation between two unsigned 8-bit values,
+/// with 8-bit fraction
+LIB8STATIC uint8_t lerp8by8( uint8_t a, uint8_t b, fract8 frac)
+{
+    uint8_t result;
+    if( b > a) {
+        uint8_t delta = b - a;
+        uint8_t scaled = scale8( delta, frac);
+        result = a + scaled;
+    } else {
+        uint8_t delta = a - b;
+        uint8_t scaled = scale8( delta, frac);
+        result = a - scaled;
+    }
+    return result;
+}
+
+/// linear interpolation between two unsigned 16-bit values,
+/// with 16-bit fraction
+LIB8STATIC uint16_t lerp16by16( uint16_t a, uint16_t b, fract16 frac)
+{
+    uint16_t result;
+    if( b > a ) {
+        uint16_t delta = b - a;
+        uint16_t scaled = scale16(delta, frac);
+        result = a + scaled;
+    } else {
+        uint16_t delta = a - b;
+        uint16_t scaled = scale16( delta, frac);
+        result = a - scaled;
+    }
+    return result;
+}
+
+/// linear interpolation between two unsigned 16-bit values,
+/// with 8-bit fraction
+LIB8STATIC uint16_t lerp16by8( uint16_t a, uint16_t b, fract8 frac)
+{
+    uint16_t result;
+    if( b > a) {
+        uint16_t delta = b - a;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a + scaled;
+    } else {
+        uint16_t delta = a - b;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a - scaled;
+    }
+    return result;
+}
+
+/// linear interpolation between two signed 15-bit values,
+/// with 8-bit fraction
+LIB8STATIC int16_t lerp15by8( int16_t a, int16_t b, fract8 frac)
+{
+    int16_t result;
+    if( b > a) {
+        uint16_t delta = b - a;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a + scaled;
+    } else {
+        uint16_t delta = a - b;
+        uint16_t scaled = scale16by8( delta, frac);
+        result = a - scaled;
+    }
+    return result;
+}
+
+/// linear interpolation between two signed 15-bit values,
+/// with 8-bit fraction
+LIB8STATIC int16_t lerp15by16( int16_t a, int16_t b, fract16 frac)
+{
+    int16_t result;
+    if( b > a) {
+        uint16_t delta = b - a;
+        uint16_t scaled = scale16( delta, frac);
+        result = a + scaled;
+    } else {
+        uint16_t delta = a - b;
+        uint16_t scaled = scale16( delta, frac);
+        result = a - scaled;
+    }
+    return result;
+}
+
+///  map8: map from one full-range 8-bit value into a narrower
+/// range of 8-bit values, possibly a range of hues.
+///
+/// E.g. map myValue into a hue in the range blue..purple..pink..red
+/// hue = map8( myValue, HUE_BLUE, HUE_RED);
+///
+/// Combines nicely with the waveform functions (like sin8, etc)
+/// to produce continuous hue gradients back and forth:
+///
+///          hue = map8( sin8( myValue), HUE_BLUE, HUE_RED);
+///
+/// Mathematically simiar to lerp8by8, but arguments are more
+/// like Arduino's "map"; this function is similar to
+///
+///          map( in, 0, 255, rangeStart, rangeEnd)
+///
+/// but faster and specifically designed for 8-bit values.
+LIB8STATIC uint8_t map8( uint8_t in, uint8_t rangeStart, uint8_t rangeEnd)
+{
+    uint8_t rangeWidth = rangeEnd - rangeStart;
+    uint8_t out = scale8( in, rangeWidth);
+    out += rangeStart;
+    return out;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// easing functions; see http://easings.net
+//
+
+/// ease8InOutQuad: 8-bit quadratic ease-in / ease-out function
+///                Takes around 13 cycles on AVR
+#if EASE8_C == 1
+LIB8STATIC uint8_t ease8InOutQuad( uint8_t i)
+{
+    uint8_t j = i;
+    if( j & 0x80 ) {
+        j = 255 - j;
+    }
+    uint8_t jj  = scale8(  j, j);
+    uint8_t jj2 = jj << 1;
+    if( i & 0x80 ) {
+        jj2 = 255 - jj2;
+    }
+    return jj2;
+}
+
+#elif EASE8_AVRASM == 1
+// This AVR asm version of ease8InOutQuad preserves one more
+// low-bit of precision than the C version, and is also slightly
+// smaller and faster.
+LIB8STATIC uint8_t ease8InOutQuad(uint8_t val) {
+    uint8_t j=val;
+    asm volatile (
+      "sbrc %[val], 7 \n"
+      "com %[j]       \n"
+      "mul %[j], %[j] \n"
+      "add r0, %[j]   \n"
+      "ldi %[j], 0    \n"
+      "adc %[j], r1   \n"
+      "lsl r0         \n" // carry = high bit of low byte of mul product
+      "rol %[j]       \n" // j = (j * 2) + carry // preserve add'l bit of precision
+      "sbrc %[val], 7 \n"
+      "com %[j]       \n"
+      "clr __zero_reg__   \n"
+      : [j] "+&a" (j)
+      : [val] "a" (val)
+      : "r0", "r1"
+      );
+    return j;
+}
+
+#else
+#error "No implementation for ease8InOutQuad available."
+#endif
+
+/// ease16InOutQuad: 16-bit quadratic ease-in / ease-out function
+// C implementation at this point
+LIB8STATIC uint16_t ease16InOutQuad( uint16_t i)
+{
+    uint16_t j = i;
+    if( j & 0x8000 ) {
+        j = 65535 - j;
+    }
+    uint16_t jj  = scale16( j, j);
+    uint16_t jj2 = jj << 1;
+    if( i & 0x8000 ) {
+        jj2 = 65535 - jj2;
+    }
+    return jj2;
+}
+
+
+/// ease8InOutCubic: 8-bit cubic ease-in / ease-out function
+///                 Takes around 18 cycles on AVR
+LIB8STATIC fract8 ease8InOutCubic( fract8 i)
+{
+    uint8_t ii  = scale8_LEAVING_R1_DIRTY(  i, i);
+    uint8_t iii = scale8_LEAVING_R1_DIRTY( ii, i);
+
+    uint16_t r1 = (3 * (uint16_t)(ii)) - ( 2 * (uint16_t)(iii));
+
+    /* the code generated for the above *'s automatically
+       cleans up R1, so there's no need to explicitily call
+       cleanup_R1(); */
+
+    uint8_t result = r1;
+
+    // if we got "256", return 255:
+    if( r1 & 0x100 ) {
+        result = 255;
+    }
+    return result;
+}
+
+/// ease8InOutApprox: fast, rough 8-bit ease-in/ease-out function
+///                   shaped approximately like 'ease8InOutCubic',
+///                   it's never off by more than a couple of percent
+///                   from the actual cubic S-curve, and it executes
+///                   more than twice as fast.  Use when the cycles
+///                   are more important than visual smoothness.
+///                   Asm version takes around 7 cycles on AVR.
+
+#if EASE8_C == 1
+LIB8STATIC fract8 ease8InOutApprox( fract8 i)
+{
+    if( i < 64) {
+        // start with slope 0.5
+        i /= 2;
+    } else if( i > (255 - 64)) {
+        // end with slope 0.5
+        i = 255 - i;
+        i /= 2;
+        i = 255 - i;
+    } else {
+        // in the middle, use slope 192/128 = 1.5
+        i -= 64;
+        i += (i / 2);
+        i += 32;
+    }
+
+    return i;
+}
+
+#elif EASE8_AVRASM == 1
+LIB8STATIC uint8_t ease8InOutApprox( fract8 i)
+{
+    // takes around 7 cycles on AVR
+    asm volatile (
+        "  subi %[i], 64         \n\t"
+        "  cpi  %[i], 128        \n\t"
+        "  brcc Lshift_%=        \n\t"
+
+        // middle case
+        "  mov __tmp_reg__, %[i] \n\t"
+        "  lsr __tmp_reg__       \n\t"
+        "  add %[i], __tmp_reg__ \n\t"
+        "  subi %[i], 224        \n\t"
+        "  rjmp Ldone_%=         \n\t"
+
+        // start or end case
+        "Lshift_%=:              \n\t"
+        "  lsr %[i]              \n\t"
+        "  subi %[i], 96         \n\t"
+
+        "Ldone_%=:               \n\t"
+
+        : [i] "+&a" (i)
+        :
+        : "r0", "r1"
+        );
+    return i;
+}
+#else
+#error "No implementation for ease8 available."
+#endif
+
+
+
+/// triwave8: triangle (sawtooth) wave generator.  Useful for
+///           turning a one-byte ever-increasing value into a
+///           one-byte value that oscillates up and down.
+///
+///           input         output
+///           0..127        0..254 (positive slope)
+///           128..255      254..0 (negative slope)
+///
+/// On AVR this function takes just three cycles.
+///
+LIB8STATIC uint8_t triwave8(uint8_t in)
+{
+    if( in & 0x80) {
+        in = 255 - in;
+    }
+    uint8_t out = in << 1;
+    return out;
+}
+
+
+// quadwave8 and cubicwave8: S-shaped wave generators (like 'sine').
+//           Useful for turning a one-byte 'counter' value into a
+//           one-byte oscillating value that moves smoothly up and down,
+//           with an 'acceleration' and 'deceleration' curve.
+//
+//           These are even faster than 'sin8', and have
+//           slightly different curve shapes.
+//
+
+/// quadwave8: quadratic waveform generator.  Spends just a little more
+///            time at the limits than 'sine' does.
+LIB8STATIC uint8_t quadwave8(uint8_t in)
+{
+    return ease8InOutQuad( triwave8( in));
+}
+
+/// cubicwave8: cubic waveform generator.  Spends visibly more time
+///             at the limits than 'sine' does.
+LIB8STATIC uint8_t cubicwave8(uint8_t in)
+{
+    return ease8InOutCubic( triwave8( in));
+}
+
+/// squarewave8: square wave generator.  Useful for
+///           turning a one-byte ever-increasing value
+///           into a one-byte value that is either 0 or 255.
+///           The width of the output 'pulse' is
+///           determined by the pulsewidth argument:
+///
+///~~~
+///           If pulsewidth is 255, output is always 255.
+///           If pulsewidth < 255, then
+///             if input < pulsewidth  then output is 255
+///             if input >= pulsewidth then output is 0
+///~~~
+///
+/// the output looking like:
+///
+///~~~
+///     255   +--pulsewidth--+
+///      .    |              |
+///      0    0              +--------(256-pulsewidth)--------
+///~~~
+///
+/// @param in
+/// @param pulsewidth
+/// @returns square wave output
+LIB8STATIC uint8_t squarewave8( uint8_t in, uint8_t pulsewidth)
+{
+    if( in < pulsewidth || (pulsewidth == 255)) {
+        return 255;
+    } else {
+        return 0;
+    }
+}
+
+
+// Beat generators - These functions produce waves at a given
+//                   number of 'beats per minute'.  Internally, they use
+//                   the Arduino function 'millis' to track elapsed time.
+//                   Accuracy is a bit better than one part in a thousand.
+//
+//       beat8( BPM ) returns an 8-bit value that cycles 'BPM' times
+//                    per minute, rising from 0 to 255, resetting to zero,
+//                    rising up again, etc..  The output of this function
+//                    is suitable for feeding directly into sin8, and cos8,
+//                    triwave8, quadwave8, and cubicwave8.
+//       beat16( BPM ) returns a 16-bit value that cycles 'BPM' times
+//                    per minute, rising from 0 to 65535, resetting to zero,
+//                    rising up again, etc.  The output of this function is
+//                    suitable for feeding directly into sin16 and cos16.
+//       beat88( BPM88) is the same as beat16, except that the BPM88 argument
+//                    MUST be in Q8.8 fixed point format, e.g. 120BPM must
+//                    be specified as 120*256 = 30720.
+//       beatsin8( BPM, uint8_t low, uint8_t high) returns an 8-bit value that
+//                    rises and falls in a sine wave, 'BPM' times per minute,
+//                    between the values of 'low' and 'high'.
+//       beatsin16( BPM, uint16_t low, uint16_t high) returns a 16-bit value
+//                    that rises and falls in a sine wave, 'BPM' times per
+//                    minute, between the values of 'low' and 'high'.
+//       beatsin88( BPM88, ...) is the same as beatsin16, except that the
+//                    BPM88 argument MUST be in Q8.8 fixed point format,
+//                    e.g. 120BPM must be specified as 120*256 = 30720.
+//
+//  BPM can be supplied two ways.  The simpler way of specifying BPM is as
+//  a simple 8-bit integer from 1-255, (e.g., "120").
+//  The more sophisticated way of specifying BPM allows for fractional
+//  "Q8.8" fixed point number (an 'accum88') with an 8-bit integer part and
+//  an 8-bit fractional part.  The easiest way to construct this is to multiply
+//  a floating point BPM value (e.g. 120.3) by 256, (e.g. resulting in 30796
+//  in this case), and pass that as the 16-bit BPM argument.
+//  "BPM88" MUST always be specified in Q8.8 format.
+//
+//  Originally designed to make an entire animation project pulse with brightness.
+//  For that effect, add this line just above your existing call to "FastLED.show()":
+//
+//     uint8_t bright = beatsin8( 60 /*BPM*/, 192 /*dimmest*/, 255 /*brightest*/ ));
+//     FastLED.setBrightness( bright );
+//     FastLED.show();
+//
+//  The entire animation will now pulse between brightness 192 and 255 once per second.
+
+
+// The beat generators need access to a millisecond counter.
+// On Arduino, this is "millis()".  On other platforms, you'll
+// need to provide a function with this signature:
+//   uint32_t get_millisecond_timer();
+// that provides similar functionality.
+// You can also force use of the get_millisecond_timer function
+// by #defining USE_GET_MILLISECOND_TIMER.
+#if (defined(ARDUINO) || defined(SPARK) || defined(FASTLED_HAS_MILLIS)) && !defined(USE_GET_MILLISECOND_TIMER)
+// Forward declaration of Arduino function 'millis'.
+//uint32_t millis();
+#define GET_MILLIS millis
+#else
+uint32_t get_millisecond_timer(void);
+#define GET_MILLIS get_millisecond_timer
+#endif
+
+// beat16 generates a 16-bit 'sawtooth' wave at a given BPM,
+///        with BPM specified in Q8.8 fixed-point format; e.g.
+///        for this function, 120 BPM MUST BE specified as
+///        120*256 = 30720.
+///        If you just want to specify "120", use beat16 or beat8.
+LIB8STATIC uint16_t beat88( accum88 beats_per_minute_88, uint32_t timebase)
+{
+    // BPM is 'beats per minute', or 'beats per 60000ms'.
+    // To avoid using the (slower) division operator, we
+    // want to convert 'beats per 60000ms' to 'beats per 65536ms',
+    // and then use a simple, fast bit-shift to divide by 65536.
+    //
+    // The ratio 65536:60000 is 279.620266667:256; we'll call it 280:256.
+    // The conversion is accurate to about 0.05%, more or less,
+    // e.g. if you ask for "120 BPM", you'll get about "119.93".
+    return (((GET_MILLIS()) - timebase) * beats_per_minute_88 * 280) >> 16;
+}
+
+/// beat16 generates a 16-bit 'sawtooth' wave at a given BPM
+LIB8STATIC uint16_t beat16( accum88 beats_per_minute, uint32_t timebase)
+{
+    // Convert simple 8-bit BPM's to full Q8.8 accum88's if needed
+    if( beats_per_minute < 256) beats_per_minute <<= 8;
+    return beat88(beats_per_minute, timebase);
+}
+
+/// beat8 generates an 8-bit 'sawtooth' wave at a given BPM
+LIB8STATIC uint8_t beat8( accum88 beats_per_minute, uint32_t timebase)
+{
+    return beat16( beats_per_minute, timebase) >> 8;
+}
+
+/// beatsin88 generates a 16-bit sine wave at a given BPM,
+///           that oscillates within a given range.
+///           For this function, BPM MUST BE SPECIFIED as
+///           a Q8.8 fixed-point value; e.g. 120BPM must be
+///           specified as 120*256 = 30720.
+///           If you just want to specify "120", use beatsin16 or beatsin8.
+LIB8STATIC uint16_t beatsin88( accum88 beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
+{
+    uint16_t beat = beat88( beats_per_minute_88, timebase);
+    uint16_t beatsin = (sin16( beat + phase_offset) + 32768);
+    uint16_t rangewidth = highest - lowest;
+    uint16_t scaledbeat = scale16( beatsin, rangewidth);
+    uint16_t result = lowest + scaledbeat;
+    return result;
+}
+
+/// beatsin16 generates a 16-bit sine wave at a given BPM,
+///           that oscillates within a given range.
+LIB8STATIC uint16_t beatsin16(accum88 beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
+{
+    uint16_t beat = beat16( beats_per_minute, timebase);
+    uint16_t beatsin = (sin16( beat + phase_offset) + 32768);
+    uint16_t rangewidth = highest - lowest;
+    uint16_t scaledbeat = scale16( beatsin, rangewidth);
+    uint16_t result = lowest + scaledbeat;
+    return result;
+}
+
+/// beatsin8 generates an 8-bit sine wave at a given BPM,
+///           that oscillates within a given range.
+LIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset)
+{
+    uint8_t beat = beat8( beats_per_minute, timebase);
+    uint8_t beatsin = sin8( beat + phase_offset);
+    uint8_t rangewidth = highest - lowest;
+    uint8_t scaledbeat = scale8( beatsin, rangewidth);
+    uint8_t result = lowest + scaledbeat;
+    return result;
+}
+
+
+/// Return the current seconds since boot in a 16-bit value.  Used as part of the
+/// "every N time-periods" mechanism
+LIB8STATIC uint16_t seconds16(void)
+{
+    uint32_t ms = GET_MILLIS();
+    uint16_t s16;
+    s16 = ms / 1000;
+    return s16;
+}
+
+/// Return the current minutes since boot in a 16-bit value.  Used as part of the
+/// "every N time-periods" mechanism
+LIB8STATIC uint16_t minutes16(void)
+{
+    uint32_t ms = GET_MILLIS();
+    uint16_t m16;
+    m16 = (ms / (60000L)) & 0xFFFF;
+    return m16;
+}
+
+/// Return the current hours since boot in an 8-bit value.  Used as part of the
+/// "every N time-periods" mechanism
+LIB8STATIC uint8_t hours8(void)
+{
+    uint32_t ms = GET_MILLIS();
+    uint8_t h8;
+    h8 = (ms / (3600000L)) & 0xFF;
+    return h8;
+}
+
+///@}
+
+#endif
diff --git a/lib/lib8tion/math8.h b/lib/lib8tion/math8.h
new file mode 100644 (file)
index 0000000..8c6b6c2
--- /dev/null
@@ -0,0 +1,552 @@
+#ifndef __INC_LIB8TION_MATH_H
+#define __INC_LIB8TION_MATH_H
+
+#include "scale8.h"
+
+///@ingroup lib8tion
+
+///@defgroup Math Basic math operations
+/// Fast, efficient 8-bit math functions specifically
+/// designed for high-performance LED programming.
+///
+/// Because of the AVR(Arduino) and ARM assembly language
+/// implementations provided, using these functions often
+/// results in smaller and faster code than the equivalent
+/// program using plain "C" arithmetic and logic.
+///@{
+
+
+/// add one byte to another, saturating at 0xFF
+/// @param i - first byte to add
+/// @param j - second byte to add
+/// @returns the sum of i & j, capped at 0xFF
+LIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j)
+{
+#if QADD8_C == 1
+    uint16_t t = i + j;
+    if (t > 255) t = 255;
+    return t;
+#elif QADD8_AVRASM == 1
+    asm volatile(
+         /* First, add j to i, conditioning the C flag */
+         "add %0, %1    \n\t"
+
+         /* Now test the C flag.
+           If C is clear, we branch around a load of 0xFF into i.
+           If C is set, we go ahead and load 0xFF into i.
+         */
+         "brcc L_%=     \n\t"
+         "ldi %0, 0xFF  \n\t"
+         "L_%=: "
+         : "+a" (i)
+         : "a"  (j) );
+    return i;
+#elif QADD8_ARM_DSP_ASM == 1
+    asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j));
+    return i;
+#else
+#error "No implementation for qadd8 available."
+#endif
+}
+
+/// Add one byte to another, saturating at 0x7F
+/// @param i - first byte to add
+/// @param j - second byte to add
+/// @returns the sum of i & j, capped at 0xFF
+LIB8STATIC_ALWAYS_INLINE int8_t qadd7( int8_t i, int8_t j)
+{
+#if QADD7_C == 1
+    int16_t t = i + j;
+    if (t > 127) t = 127;
+    return t;
+#elif QADD7_AVRASM == 1
+    asm volatile(
+         /* First, add j to i, conditioning the V flag */
+         "add %0, %1    \n\t"
+
+         /* Now test the V flag.
+          If V is clear, we branch around a load of 0x7F into i.
+          If V is set, we go ahead and load 0x7F into i.
+          */
+         "brvc L_%=     \n\t"
+         "ldi %0, 0x7F  \n\t"
+         "L_%=: "
+         : "+a" (i)
+         : "a"  (j) );
+
+    return i;
+#elif QADD7_ARM_DSP_ASM == 1
+    asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j));
+    return i;
+#else
+#error "No implementation for qadd7 available."
+#endif
+}
+
+/// subtract one byte from another, saturating at 0x00
+/// @returns i - j with a floor of 0
+LIB8STATIC_ALWAYS_INLINE uint8_t qsub8( uint8_t i, uint8_t j)
+{
+#if QSUB8_C == 1
+    int16_t t = i - j;
+    if (t < 0) t = 0;
+    return t;
+#elif QSUB8_AVRASM == 1
+
+    asm volatile(
+         /* First, subtract j from i, conditioning the C flag */
+         "sub %0, %1    \n\t"
+
+         /* Now test the C flag.
+          If C is clear, we branch around a load of 0x00 into i.
+          If C is set, we go ahead and load 0x00 into i.
+          */
+         "brcc L_%=     \n\t"
+         "ldi %0, 0x00  \n\t"
+         "L_%=: "
+         : "+a" (i)
+         : "a"  (j) );
+
+    return i;
+#else
+#error "No implementation for qsub8 available."
+#endif
+}
+
+/// add one byte to another, with one byte result
+LIB8STATIC_ALWAYS_INLINE uint8_t add8( uint8_t i, uint8_t j)
+{
+#if ADD8_C == 1
+    uint16_t t = i + j;
+    return t;
+#elif ADD8_AVRASM == 1
+    // Add j to i, period.
+    asm volatile( "add %0, %1" : "+a" (i) : "a" (j));
+    return i;
+#else
+#error "No implementation for add8 available."
+#endif
+}
+
+/// add one byte to another, with one byte result
+LIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j)
+{
+#if ADD8_C == 1
+    uint16_t t = i + j;
+    return t;
+#elif ADD8_AVRASM == 1
+    // Add i(one byte) to j(two bytes)
+    asm volatile( "add %A[j], %[i]              \n\t"
+                  "adc %B[j], __zero_reg__      \n\t"
+                 : [j] "+a" (j)
+                 : [i] "a"  (i)
+                 );
+    return i;
+#else
+#error "No implementation for add8to16 available."
+#endif
+}
+
+
+/// subtract one byte from another, 8-bit result
+LIB8STATIC_ALWAYS_INLINE uint8_t sub8( uint8_t i, uint8_t j)
+{
+#if SUB8_C == 1
+    int16_t t = i - j;
+    return t;
+#elif SUB8_AVRASM == 1
+    // Subtract j from i, period.
+    asm volatile( "sub %0, %1" : "+a" (i) : "a" (j));
+    return i;
+#else
+#error "No implementation for sub8 available."
+#endif
+}
+
+/// Calculate an integer average of two unsigned
+///       8-bit integer values (uint8_t).
+///       Fractional results are rounded down, e.g. avg8(20,41) = 30
+LIB8STATIC_ALWAYS_INLINE uint8_t avg8( uint8_t i, uint8_t j)
+{
+#if AVG8_C == 1
+    return (i + j) >> 1;
+#elif AVG8_AVRASM == 1
+    asm volatile(
+         /* First, add j to i, 9th bit overflows into C flag */
+         "add %0, %1    \n\t"
+         /* Divide by two, moving C flag into high 8th bit */
+         "ror %0        \n\t"
+         : "+a" (i)
+         : "a"  (j) );
+    return i;
+#else
+#error "No implementation for avg8 available."
+#endif
+}
+
+/// Calculate an integer average of two unsigned
+///       16-bit integer values (uint16_t).
+///       Fractional results are rounded down, e.g. avg16(20,41) = 30
+LIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j)
+{
+#if AVG16_C == 1
+    return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1;
+#elif AVG16_AVRASM == 1
+    asm volatile(
+                 /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */
+                 "add %A[i], %A[j]    \n\t"
+                 /* Now, add C + jHi to iHi, 17th bit overflows into C flag */
+                 "adc %B[i], %B[j]    \n\t"
+                 /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */
+                 "ror %B[i]        \n\t"
+                 /* Divide iLo by two, moving C flag into high 8th bit */
+                 "ror %A[i]        \n\t"
+                 : [i] "+a" (i)
+                 : [j] "a"  (j) );
+    return i;
+#else
+#error "No implementation for avg16 available."
+#endif
+}
+
+
+/// Calculate an integer average of two signed 7-bit
+///       integers (int8_t)
+///       If the first argument is even, result is rounded down.
+///       If the first argument is odd, result is result up.
+LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j)
+{
+#if AVG7_C == 1
+    return ((i + j) >> 1) + (i & 0x1);
+#elif AVG7_AVRASM == 1
+    asm volatile(
+                 "asr %1        \n\t"
+                 "asr %0        \n\t"
+                 "adc %0, %1    \n\t"
+                 : "+a" (i)
+                 : "a"  (j) );
+    return i;
+#else
+#error "No implementation for avg7 available."
+#endif
+}
+
+/// Calculate an integer average of two signed 15-bit
+///       integers (int16_t)
+///       If the first argument is even, result is rounded down.
+///       If the first argument is odd, result is result up.
+LIB8STATIC_ALWAYS_INLINE int16_t avg15( int16_t i, int16_t j)
+{
+#if AVG15_C == 1
+    return ((int32_t)((int32_t)(i) + (int32_t)(j)) >> 1) + (i & 0x1);
+#elif AVG15_AVRASM == 1
+    asm volatile(
+                 /* first divide j by 2, throwing away lowest bit */
+                 "asr %B[j]          \n\t"
+                 "ror %A[j]          \n\t"
+                 /* now divide i by 2, with lowest bit going into C */
+                 "asr %B[i]          \n\t"
+                 "ror %A[i]          \n\t"
+                 /* add j + C to i */
+                 "adc %A[i], %A[j]   \n\t"
+                 "adc %B[i], %B[j]   \n\t"
+                 : [i] "+a" (i)
+                 : [j] "a"  (j) );
+    return i;
+#else
+#error "No implementation for avg15 available."
+#endif
+}
+
+
+///       Calculate the remainder of one unsigned 8-bit
+///       value divided by anoter, aka A % M.
+///       Implemented by repeated subtraction, which is
+///       very compact, and very fast if A is 'probably'
+///       less than M.  If A is a large multiple of M,
+///       the loop has to execute multiple times.  However,
+///       even in that case, the loop is only two
+///       instructions long on AVR, i.e., quick.
+LIB8STATIC_ALWAYS_INLINE uint8_t mod8( uint8_t a, uint8_t m)
+{
+#if defined(__AVR__)
+    asm volatile (
+                  "L_%=:  sub %[a],%[m]    \n\t"
+                  "       brcc L_%=        \n\t"
+                  "       add %[a],%[m]    \n\t"
+                  : [a] "+r" (a)
+                  : [m] "r"  (m)
+                  );
+#else
+    while( a >= m) a -= m;
+#endif
+    return a;
+}
+
+///          Add two numbers, and calculate the modulo
+///          of the sum and a third number, M.
+///          In other words, it returns (A+B) % M.
+///          It is designed as a compact mechanism for
+///          incrementing a 'mode' switch and wrapping
+///          around back to 'mode 0' when the switch
+///          goes past the end of the available range.
+///          e.g. if you have seven modes, this switches
+///          to the next one and wraps around if needed:
+///            mode = addmod8( mode, 1, 7);
+///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.
+LIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m)
+{
+#if defined(__AVR__)
+    asm volatile (
+                  "       add %[a],%[b]    \n\t"
+                  "L_%=:  sub %[a],%[m]    \n\t"
+                  "       brcc L_%=        \n\t"
+                  "       add %[a],%[m]    \n\t"
+                  : [a] "+r" (a)
+                  : [b] "r"  (b), [m] "r" (m)
+                  );
+#else
+    a += b;
+    while( a >= m) a -= m;
+#endif
+    return a;
+}
+
+///          Subtract two numbers, and calculate the modulo
+///          of the difference and a third number, M.
+///          In other words, it returns (A-B) % M.
+///          It is designed as a compact mechanism for
+///          incrementing a 'mode' switch and wrapping
+///          around back to 'mode 0' when the switch
+///          goes past the end of the available range.
+///          e.g. if you have seven modes, this switches
+///          to the next one and wraps around if needed:
+///            mode = addmod8( mode, 1, 7);
+///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.
+LIB8STATIC uint8_t submod8( uint8_t a, uint8_t b, uint8_t m)
+{
+#if defined(__AVR__)
+    asm volatile (
+                  "       sub %[a],%[b]    \n\t"
+                  "L_%=:  sub %[a],%[m]    \n\t"
+                  "       brcc L_%=        \n\t"
+                  "       add %[a],%[m]    \n\t"
+                  : [a] "+r" (a)
+                  : [b] "r"  (b), [m] "r" (m)
+                  );
+#else
+    a -= b;
+    while( a >= m) a -= m;
+#endif
+    return a;
+}
+
+/// 8x8 bit multiplication, with 8 bit result
+LIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j)
+{
+#if MUL8_C == 1
+    return ((uint16_t)i * (uint16_t)(j) ) & 0xFF;
+#elif MUL8_AVRASM == 1
+    asm volatile(
+         /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
+         "mul %0, %1          \n\t"
+         /* Extract the LOW 8-bits (r0) */
+         "mov %0, r0          \n\t"
+         /* Restore r1 to "0"; it's expected to always be that */
+         "clr __zero_reg__    \n\t"
+         : "+a" (i)
+         : "a"  (j)
+         : "r0", "r1");
+
+    return i;
+#else
+#error "No implementation for mul8 available."
+#endif
+}
+
+
+/// saturating 8x8 bit multiplication, with 8 bit result
+/// @returns the product of i * j, capping at 0xFF
+LIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j)
+{
+#if QMUL8_C == 1
+    int p = ((uint16_t)i * (uint16_t)(j) );
+    if( p > 255) p = 255;
+    return p;
+#elif QMUL8_AVRASM == 1
+    asm volatile(
+                 /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
+                 "  mul %0, %1          \n\t"
+                 /* If high byte of result is zero, all is well. */
+                 "  tst r1              \n\t"
+                 "  breq Lnospill_%=    \n\t"
+                 /* If high byte of result > 0, saturate low byte to 0xFF */
+                 "  ldi %0,0xFF         \n\t"
+                 "  rjmp Ldone_%=       \n\t"
+                 "Lnospill_%=:          \n\t"
+                 /* Extract the LOW 8-bits (r0) */
+                 "  mov %0, r0          \n\t"
+                 "Ldone_%=:             \n\t"
+                 /* Restore r1 to "0"; it's expected to always be that */
+                 "  clr __zero_reg__    \n\t"
+                 : "+a" (i)
+                 : "a"  (j)
+                 : "r0", "r1");
+
+    return i;
+#else
+#error "No implementation for qmul8 available."
+#endif
+}
+
+
+/// take abs() of a signed 8-bit uint8_t
+LIB8STATIC_ALWAYS_INLINE int8_t abs8( int8_t i)
+{
+#if ABS8_C == 1
+    if( i < 0) i = -i;
+    return i;
+#elif ABS8_AVRASM == 1
+
+
+    asm volatile(
+         /* First, check the high bit, and prepare to skip if it's clear */
+         "sbrc %0, 7 \n"
+
+         /* Negate the value */
+         "neg %0     \n"
+
+         : "+r" (i) : "r" (i) );
+    return i;
+#else
+#error "No implementation for abs8 available."
+#endif
+}
+
+///         square root for 16-bit integers
+///         About three times faster and five times smaller
+///         than Arduino's general sqrt on AVR.
+LIB8STATIC uint8_t sqrt16(uint16_t x)
+{
+    if( x <= 1) {
+        return x;
+    }
+
+    uint8_t low = 1; // lower bound
+    uint8_t hi, mid;
+
+    if( x > 7904) {
+        hi = 255;
+    } else {
+        hi = (x >> 5) + 8; // initial estimate for upper bound
+    }
+
+    do {
+        mid = (low + hi) >> 1;
+        if ((uint16_t)(mid * mid) > x) {
+            hi = mid - 1;
+        } else {
+            if( mid == 255) {
+                return 255;
+            }
+            low = mid + 1;
+        }
+    } while (hi >= low);
+
+    return low - 1;
+}
+
+/// blend a variable proproportion(0-255) of one byte to another
+/// @param a - the starting byte value
+/// @param b - the byte value to blend toward
+/// @param amountOfB - the proportion (0-255) of b to blend
+/// @returns a byte value between a and b, inclusive
+#if (FASTLED_BLEND_FIXED == 1)
+LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
+{
+#if BLEND8_C == 1
+    uint16_t partial;
+    uint8_t result;
+
+    uint8_t amountOfA = 255 - amountOfB;
+
+    partial = (a * amountOfA);
+#if (FASTLED_SCALE8_FIXED == 1)
+    partial += a;
+    //partial = add8to16( a, partial);
+#endif
+
+    partial += (b * amountOfB);
+#if (FASTLED_SCALE8_FIXED == 1)
+    partial += b;
+    //partial = add8to16( b, partial);
+#endif
+
+    result = partial >> 8;
+
+    return result;
+
+#elif BLEND8_AVRASM == 1
+    uint16_t partial;
+    uint8_t result;
+
+    asm volatile (
+        /* partial = b * amountOfB */
+        "  mul %[b], %[amountOfB]        \n\t"
+        "  movw %A[partial], r0          \n\t"
+
+        /* amountOfB (aka amountOfA) = 255 - amountOfB */
+        "  com %[amountOfB]              \n\t"
+
+        /* partial += a * amountOfB (aka amountOfA) */
+        "  mul %[a], %[amountOfB]        \n\t"
+
+        "  add %A[partial], r0           \n\t"
+        "  adc %B[partial], r1           \n\t"
+
+        "  clr __zero_reg__              \n\t"
+
+#if (FASTLED_SCALE8_FIXED == 1)
+        /* partial += a */
+        "  add %A[partial], %[a]         \n\t"
+        "  adc %B[partial], __zero_reg__ \n\t"
+
+        // partial += b
+        "  add %A[partial], %[b]         \n\t"
+        "  adc %B[partial], __zero_reg__ \n\t"
+#endif
+
+        : [partial] "=r" (partial),
+          [amountOfB] "+a" (amountOfB)
+        : [a] "a" (a),
+          [b] "a" (b)
+        : "r0", "r1"
+    );
+
+    result = partial >> 8;
+
+    return result;
+
+#else
+#error "No implementation for blend8 available."
+#endif
+}
+
+#else
+LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
+{
+    // This version loses precision in the integer math
+    // and can actually return results outside of the range
+    // from a to b.  Its use is not recommended.
+    uint8_t result;
+    uint8_t amountOfA = 255 - amountOfB;
+    result = scale8_LEAVING_R1_DIRTY( a, amountOfA)
+           + scale8_LEAVING_R1_DIRTY( b, amountOfB);
+    cleanup_R1();
+    return result;
+}
+#endif
+
+
+///@}
+#endif
diff --git a/lib/lib8tion/random8.h b/lib/lib8tion/random8.h
new file mode 100644 (file)
index 0000000..7ee67cb
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef __INC_LIB8TION_RANDOM_H
+#define __INC_LIB8TION_RANDOM_H
+///@ingroup lib8tion
+
+///@defgroup Random Fast random number generators
+/// Fast 8- and 16- bit unsigned random numbers.
+///  Significantly faster than Arduino random(), but
+///  also somewhat less random.  You can add entropy.
+///@{
+
+// X(n+1) = (2053 * X(n)) + 13849)
+#define FASTLED_RAND16_2053  ((uint16_t)(2053))
+#define FASTLED_RAND16_13849 ((uint16_t)(13849))
+
+/// random number seed
+extern uint16_t rand16seed;// = RAND16_SEED;
+
+/// Generate an 8-bit random number
+LIB8STATIC uint8_t random8(void)
+{
+    rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;
+    // return the sum of the high and low bytes, for better
+    //  mixing and non-sequential correlation
+    return (uint8_t)(((uint8_t)(rand16seed & 0xFF)) +
+                     ((uint8_t)(rand16seed >> 8)));
+}
+
+/// Generate a 16 bit random number
+LIB8STATIC uint16_t random16(void)
+{
+    rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;
+    return rand16seed;
+}
+
+/// Generate an 8-bit random number between 0 and lim
+/// @param lim the upper bound for the result
+LIB8STATIC uint8_t random8_max(uint8_t lim)
+{
+    uint8_t r = random8();
+    r = (r*lim) >> 8;
+    return r;
+}
+
+/// Generate an 8-bit random number in the given range
+/// @param min the lower bound for the random number
+/// @param lim the upper bound for the random number
+LIB8STATIC uint8_t random8_min_max(uint8_t min, uint8_t lim)
+{
+    uint8_t delta = lim - min;
+    uint8_t r = random8_max(delta) + min;
+    return r;
+}
+
+/// Generate an 16-bit random number between 0 and lim
+/// @param lim the upper bound for the result
+LIB8STATIC uint16_t random16_max(uint16_t lim)
+{
+    uint16_t r = random16();
+    uint32_t p = (uint32_t)lim * (uint32_t)r;
+    r = p >> 16;
+    return r;
+}
+
+/// Generate an 16-bit random number in the given range
+/// @param min the lower bound for the random number
+/// @param lim the upper bound for the random number
+LIB8STATIC uint16_t random16_min_max( uint16_t min, uint16_t lim)
+{
+    uint16_t delta = lim - min;
+    uint16_t r = random16_max(delta) + min;
+    return r;
+}
+
+/// Set the 16-bit seed used for the random number generator
+LIB8STATIC void random16_set_seed(uint16_t seed)
+{
+    rand16seed = seed;
+}
+
+/// Get the current seed value for the random number generator
+LIB8STATIC uint16_t random16_get_seed(void)
+{
+    return rand16seed;
+}
+
+/// Add entropy into the random number generator
+LIB8STATIC void random16_add_entropy(uint16_t entropy)
+{
+    rand16seed += entropy;
+}
+
+///@}
+
+#endif
diff --git a/lib/lib8tion/scale8.h b/lib/lib8tion/scale8.h
new file mode 100644 (file)
index 0000000..9895fd4
--- /dev/null
@@ -0,0 +1,542 @@
+#ifndef __INC_LIB8TION_SCALE_H
+#define __INC_LIB8TION_SCALE_H
+
+///@ingroup lib8tion
+
+///@defgroup Scaling Scaling functions
+/// Fast, efficient 8-bit scaling functions specifically
+/// designed for high-performance LED programming.
+///
+/// Because of the AVR(Arduino) and ARM assembly language
+/// implementations provided, using these functions often
+/// results in smaller and faster code than the equivalent
+/// program using plain "C" arithmetic and logic.
+///@{
+
+///  scale one byte by a second one, which is treated as
+///  the numerator of a fraction whose denominator is 256
+///  In other words, it computes i * (scale / 256)
+///  4 clocks AVR with MUL, 2 clocks ARM
+LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+#if (FASTLED_SCALE8_FIXED == 1)
+    return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8;
+#else
+    return ((uint16_t)i * (uint16_t)(scale) ) >> 8;
+#endif
+#elif SCALE8_AVRASM == 1
+#if defined(LIB8_ATTINY)
+#if (FASTLED_SCALE8_FIXED == 1)
+    uint8_t work=i;
+#else
+    uint8_t work=0;
+#endif
+    uint8_t cnt=0x80;
+    asm volatile(
+#if (FASTLED_SCALE8_FIXED == 1)
+        "  inc %[scale]                 \n\t"
+        "  breq DONE_%=                 \n\t"
+        "  clr %[work]                  \n\t"
+#endif
+        "LOOP_%=:                       \n\t"
+        /*"  sbrc %[scale], 0             \n\t"
+        "  add %[work], %[i]            \n\t"
+        "  ror %[work]                  \n\t"
+        "  lsr %[scale]                 \n\t"
+        "  clc                          \n\t"*/
+        "  sbrc %[scale], 0             \n\t"
+        "  add %[work], %[i]            \n\t"
+        "  ror %[work]                  \n\t"
+        "  lsr %[scale]                 \n\t"
+        "  lsr %[cnt]                   \n\t"
+        "brcc LOOP_%=                   \n\t"
+        "DONE_%=:                       \n\t"
+        : [work] "+r" (work), [cnt] "+r" (cnt)
+        : [scale] "r" (scale), [i] "r" (i)
+        :
+      );
+    return work;
+#else
+    asm volatile(
+#if (FASTLED_SCALE8_FIXED==1)
+        // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
+        "mul %0, %1          \n\t"
+        // Add i to r0, possibly setting the carry flag
+        "add r0, %0         \n\t"
+        // load the immediate 0 into i (note, this does _not_ touch any flags)
+        "ldi %0, 0x00       \n\t"
+        // walk and chew gum at the same time
+        "adc %0, r1          \n\t"
+#else
+         /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+         "mul %0, %1          \n\t"
+         /* Move the high 8-bits of the product (r1) back to i */
+         "mov %0, r1          \n\t"
+         /* Restore r1 to "0"; it's expected to always be that */
+#endif
+         "clr __zero_reg__    \n\t"
+
+         : "+a" (i)      /* writes to i */
+         : "a"  (scale)  /* uses scale */
+         : "r0", "r1"    /* clobbers r0, r1 */ );
+
+    /* Return the result */
+    return i;
+#endif
+#else
+#error "No implementation for scale8 available."
+#endif
+}
+
+
+///  The "video" version of scale8 guarantees that the output will
+///  be only be zero if one or both of the inputs are zero.  If both
+///  inputs are non-zero, the output is guaranteed to be non-zero.
+///  This makes for better 'video'/LED dimming, at the cost of
+///  several additional cycles.
+LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1 || defined(LIB8_ATTINY)
+    uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
+    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+    return j;
+#elif SCALE8_AVRASM == 1
+    uint8_t j=0;
+    asm volatile(
+        "  tst %[i]\n\t"
+        "  breq L_%=\n\t"
+        "  mul %[i], %[scale]\n\t"
+        "  mov %[j], r1\n\t"
+        "  clr __zero_reg__\n\t"
+        "  cpse %[scale], r1\n\t"
+        "  subi %[j], 0xFF\n\t"
+        "L_%=: \n\t"
+        : [j] "+a" (j)
+        : [i] "a" (i), [scale] "a" (scale)
+        : "r0", "r1");
+
+    return j;
+    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    // asm volatile(
+    //      "      tst %0           \n"
+    //      "      breq L_%=        \n"
+    //      "      mul %0, %1       \n"
+    //      "      mov %0, r1       \n"
+    //      "      add %0, %2       \n"
+    //      "      clr __zero_reg__ \n"
+    //      "L_%=:                  \n"
+
+    //      : "+a" (i)
+    //      : "a" (scale), "a" (nonzeroscale)
+    //      : "r0", "r1");
+
+    // // Return the result
+    // return i;
+#else
+#error "No implementation for scale8_video available."
+#endif
+}
+
+
+/// This version of scale8 does not clean up the R1 register on AVR
+/// If you are doing several 'scale8's in a row, use this, and
+/// then explicitly call cleanup_R1.
+LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1
+#if (FASTLED_SCALE8_FIXED == 1)
+    return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;
+#else
+    return ((int)i * (int)(scale) ) >> 8;
+#endif
+#elif SCALE8_AVRASM == 1
+    asm volatile(
+      #if (FASTLED_SCALE8_FIXED==1)
+              // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
+              "mul %0, %1          \n\t"
+              // Add i to r0, possibly setting the carry flag
+              "add r0, %0         \n\t"
+              // load the immediate 0 into i (note, this does _not_ touch any flags)
+              "ldi %0, 0x00       \n\t"
+              // walk and chew gum at the same time
+              "adc %0, r1          \n\t"
+      #else
+         /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
+         "mul %0, %1    \n\t"
+         /* Move the high 8-bits of the product (r1) back to i */
+         "mov %0, r1    \n\t"
+      #endif
+         /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF  */
+         /* "clr __zero_reg__    \n\t" */
+
+         : "+a" (i)      /* writes to i */
+         : "a"  (scale)  /* uses scale */
+         : "r0", "r1"    /* clobbers r0, r1 */ );
+
+    // Return the result
+    return i;
+#else
+#error "No implementation for scale8_LEAVING_R1_DIRTY available."
+#endif
+}
+
+
+/// This version of scale8_video does not clean up the R1 register on AVR
+/// If you are doing several 'scale8_video's in a row, use this, and
+/// then explicitly call cleanup_R1.
+LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
+{
+#if SCALE8_C == 1 || defined(LIB8_ATTINY)
+    uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
+    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
+    return j;
+#elif SCALE8_AVRASM == 1
+    uint8_t j=0;
+    asm volatile(
+        "  tst %[i]\n\t"
+        "  breq L_%=\n\t"
+        "  mul %[i], %[scale]\n\t"
+        "  mov %[j], r1\n\t"
+        "  breq L_%=\n\t"
+        "  subi %[j], 0xFF\n\t"
+        "L_%=: \n\t"
+        : [j] "+a" (j)
+        : [i] "a" (i), [scale] "a" (scale)
+        : "r0", "r1");
+
+    return j;
+    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
+    // asm volatile(
+    //      "      tst %0           \n"
+    //      "      breq L_%=        \n"
+    //      "      mul %0, %1       \n"
+    //      "      mov %0, r1       \n"
+    //      "      add %0, %2       \n"
+    //      "      clr __zero_reg__ \n"
+    //      "L_%=:                  \n"
+
+    //      : "+a" (i)
+    //      : "a" (scale), "a" (nonzeroscale)
+    //      : "r0", "r1");
+
+    // // Return the result
+    // return i;
+#else
+#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
+#endif
+}
+
+/// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls
+LIB8STATIC_ALWAYS_INLINE void cleanup_R1(void)
+{
+#if CLEANUP_R1_AVRASM == 1
+    // Restore r1 to "0"; it's expected to always be that
+    asm volatile( "clr __zero_reg__  \n\t" : : : "r1" );
+#endif
+}
+
+
+/// scale a 16-bit unsigned value by an 8-bit value,
+///         considered as numerator of a fraction whose denominator
+///         is 256. In other words, it computes i * (scale / 256)
+
+LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale )
+{
+#if SCALE16BY8_C == 1
+    uint16_t result;
+#if FASTLED_SCALE8_FIXED == 1
+    result = (i * (1+((uint16_t)scale))) >> 8;
+#else
+    result = (i * scale) / 256;
+#endif
+    return result;
+#elif SCALE16BY8_AVRASM == 1
+#if FASTLED_SCALE8_FIXED == 1
+    uint16_t result = 0;
+    asm volatile(
+                 // result.A = HighByte( (i.A x scale) + i.A )
+                 "  mul %A[i], %[scale]                 \n\t"
+                 "  add r0, %A[i]                       \n\t"
+            //   "  adc r1, [zero]                      \n\t"
+            //   "  mov %A[result], r1                  \n\t"
+                 "  adc %A[result], r1                  \n\t"
+
+                 // result.A-B += i.B x scale
+                 "  mul %B[i], %[scale]                 \n\t"
+                 "  add %A[result], r0                  \n\t"
+                 "  adc %B[result], r1                  \n\t"
+
+                 // cleanup r1
+                 "  clr __zero_reg__                    \n\t"
+
+                 // result.A-B += i.B
+                 "  add %A[result], %B[i]               \n\t"
+                 "  adc %B[result], __zero_reg__        \n\t"
+
+                 : [result] "+r" (result)
+                 : [i] "r" (i), [scale] "r" (scale)
+                 : "r0", "r1"
+                 );
+    return result;
+#else
+    uint16_t result = 0;
+    asm volatile(
+         // result.A = HighByte(i.A x j )
+         "  mul %A[i], %[scale]                 \n\t"
+         "  mov %A[result], r1                  \n\t"
+         //"  clr %B[result]                      \n\t"
+
+         // result.A-B += i.B x j
+         "  mul %B[i], %[scale]                 \n\t"
+         "  add %A[result], r0                  \n\t"
+         "  adc %B[result], r1                  \n\t"
+
+         // cleanup r1
+         "  clr __zero_reg__                    \n\t"
+
+         : [result] "+r" (result)
+         : [i] "r" (i), [scale] "r" (scale)
+         : "r0", "r1"
+         );
+    return result;
+#endif
+#else
+    #error "No implementation for scale16by8 available."
+#endif
+}
+
+/// scale a 16-bit unsigned value by a 16-bit value,
+///         considered as numerator of a fraction whose denominator
+///         is 65536. In other words, it computes i * (scale / 65536)
+
+LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
+{
+  #if SCALE16_C == 1
+    uint16_t result;
+#if FASTLED_SCALE8_FIXED == 1
+    result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536;
+#else
+    result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
+#endif
+    return result;
+#elif SCALE16_AVRASM == 1
+#if FASTLED_SCALE8_FIXED == 1
+    // implemented sort of like
+    //   result = ((i * scale) + i ) / 65536
+    //
+    // why not like this, you may ask?
+    //   result = (i * (scale+1)) / 65536
+    // the answer is that if scale is 65535, then scale+1
+    // will be zero, which is not what we want.
+    uint32_t result;
+    asm volatile(
+                 // result.A-B  = i.A x scale.A
+                 "  mul %A[i], %A[scale]                 \n\t"
+                 //  save results...
+                 // basic idea:
+                 //"  mov %A[result], r0                 \n\t"
+                 //"  mov %B[result], r1                 \n\t"
+                 // which can be written as...
+                 "  movw %A[result], r0                   \n\t"
+                 // Because we're going to add i.A-B to
+                 // result.A-D, we DO need to keep both
+                 // the r0 and r1 portions of the product
+                 // UNlike in the 'unfixed scale8' version.
+                 // So the movw here is needed.
+                 : [result] "=r" (result)
+                 : [i] "r" (i),
+                 [scale] "r" (scale)
+                 : "r0", "r1"
+                 );
+
+    asm volatile(
+                 // result.C-D  = i.B x scale.B
+                 "  mul %B[i], %B[scale]                 \n\t"
+                 //"  mov %C[result], r0                 \n\t"
+                 //"  mov %D[result], r1                 \n\t"
+                 "  movw %C[result], r0                   \n\t"
+                 : [result] "+r" (result)
+                 : [i] "r" (i),
+                 [scale] "r" (scale)
+                 : "r0", "r1"
+                 );
+
+    const uint8_t  zero = 0;
+    asm volatile(
+                 // result.B-D += i.B x scale.A
+                 "  mul %B[i], %A[scale]                 \n\t"
+
+                 "  add %B[result], r0                   \n\t"
+                 "  adc %C[result], r1                   \n\t"
+                 "  adc %D[result], %[zero]              \n\t"
+
+                 // result.B-D += i.A x scale.B
+                 "  mul %A[i], %B[scale]                 \n\t"
+
+                 "  add %B[result], r0                   \n\t"
+                 "  adc %C[result], r1                   \n\t"
+                 "  adc %D[result], %[zero]              \n\t"
+
+                 // cleanup r1
+                 "  clr r1                               \n\t"
+
+                 : [result] "+r" (result)
+                 : [i] "r" (i),
+                 [scale] "r" (scale),
+                 [zero] "r" (zero)
+                 : "r0", "r1"
+                 );
+
+    asm volatile(
+                 // result.A-D += i.A-B
+                 "  add %A[result], %A[i]                \n\t"
+                 "  adc %B[result], %B[i]                \n\t"
+                 "  adc %C[result], %[zero]              \n\t"
+                 "  adc %D[result], %[zero]              \n\t"
+                 : [result] "+r" (result)
+                 : [i] "r" (i),
+                 [zero] "r" (zero)
+                 );
+
+    result = result >> 16;
+    return result;
+#else
+    uint32_t result;
+    asm volatile(
+                 // result.A-B  = i.A x scale.A
+                 "  mul %A[i], %A[scale]                 \n\t"
+                 //  save results...
+                 // basic idea:
+                 //"  mov %A[result], r0                 \n\t"
+                 //"  mov %B[result], r1                 \n\t"
+                 // which can be written as...
+                 "  movw %A[result], r0                   \n\t"
+                 // We actually don't need to do anything with r0,
+                 // as result.A is never used again here, so we
+                 // could just move the high byte, but movw is
+                 // one clock cycle, just like mov, so might as
+                 // well, in case we want to use this code for
+                 // a generic 16x16 multiply somewhere.
+
+                 : [result] "=r" (result)
+                 : [i] "r" (i),
+                   [scale] "r" (scale)
+                 : "r0", "r1"
+                 );
+
+    asm volatile(
+                 // result.C-D  = i.B x scale.B
+                 "  mul %B[i], %B[scale]                 \n\t"
+                 //"  mov %C[result], r0                 \n\t"
+                 //"  mov %D[result], r1                 \n\t"
+                 "  movw %C[result], r0                   \n\t"
+                 : [result] "+r" (result)
+                 : [i] "r" (i),
+                   [scale] "r" (scale)
+                 : "r0", "r1"
+                 );
+
+    const uint8_t  zero = 0;
+    asm volatile(
+                 // result.B-D += i.B x scale.A
+                 "  mul %B[i], %A[scale]                 \n\t"
+
+                 "  add %B[result], r0                   \n\t"
+                 "  adc %C[result], r1                   \n\t"
+                 "  adc %D[result], %[zero]              \n\t"
+
+                 // result.B-D += i.A x scale.B
+                 "  mul %A[i], %B[scale]                 \n\t"
+
+                 "  add %B[result], r0                   \n\t"
+                 "  adc %C[result], r1                   \n\t"
+                 "  adc %D[result], %[zero]              \n\t"
+
+                 // cleanup r1
+                 "  clr r1                               \n\t"
+
+                 : [result] "+r" (result)
+                 : [i] "r" (i),
+                   [scale] "r" (scale),
+                   [zero] "r" (zero)
+                 : "r0", "r1"
+                 );
+
+    result = result >> 16;
+    return result;
+#endif
+#else
+    #error "No implementation for scale16 available."
+#endif
+}
+///@}
+
+///@defgroup Dimming Dimming and brightening functions
+///
+/// Dimming and brightening functions
+///
+/// The eye does not respond in a linear way to light.
+/// High speed PWM'd LEDs at 50% duty cycle appear far
+/// brighter then the 'half as bright' you might expect.
+///
+/// If you want your midpoint brightness leve (128) to
+/// appear half as bright as 'full' brightness (255), you
+/// have to apply a 'dimming function'.
+///@{
+
+/// Adjust a scaling value for dimming
+LIB8STATIC uint8_t dim8_raw( uint8_t x)
+{
+    return scale8( x, x);
+}
+
+/// Adjust a scaling value for dimming for video (value will never go below 1)
+LIB8STATIC uint8_t dim8_video( uint8_t x)
+{
+    return scale8_video( x, x);
+}
+
+/// Linear version of the dimming function that halves for values < 128
+LIB8STATIC uint8_t dim8_lin( uint8_t x )
+{
+    if( x & 0x80 ) {
+        x = scale8( x, x);
+    } else {
+        x += 1;
+        x /= 2;
+    }
+    return x;
+}
+
+/// inverse of the dimming function, brighten a value
+LIB8STATIC uint8_t brighten8_raw( uint8_t x)
+{
+    uint8_t ix = 255 - x;
+    return 255 - scale8( ix, ix);
+}
+
+/// inverse of the dimming function, brighten a value
+LIB8STATIC uint8_t brighten8_video( uint8_t x)
+{
+    uint8_t ix = 255 - x;
+    return 255 - scale8_video( ix, ix);
+}
+
+/// inverse of the dimming function, brighten a value
+LIB8STATIC uint8_t brighten8_lin( uint8_t x )
+{
+    uint8_t ix = 255 - x;
+    if( ix & 0x80 ) {
+        ix = scale8( ix, ix);
+    } else {
+        ix += 1;
+        ix /= 2;
+    }
+    return 255 - ix;
+}
+
+///@}
+#endif
diff --git a/lib/lib8tion/trig8.h b/lib/lib8tion/trig8.h
new file mode 100644 (file)
index 0000000..4907c6f
--- /dev/null
@@ -0,0 +1,259 @@
+#ifndef __INC_LIB8TION_TRIG_H
+#define __INC_LIB8TION_TRIG_H
+
+///@ingroup lib8tion
+
+///@defgroup Trig Fast trig functions
+/// Fast 8 and 16-bit approximations of sin(x) and cos(x).
+///        Don't use these approximations for calculating the
+///        trajectory of a rocket to Mars, but they're great
+///        for art projects and LED displays.
+///
+///        On Arduino/AVR, the 16-bit approximation is more than
+///        10X faster than floating point sin(x) and cos(x), while
+/// the 8-bit approximation is more than 20X faster.
+///@{
+
+#if defined(__AVR__)
+#define sin16 sin16_avr
+#else
+#define sin16 sin16_C
+#endif
+
+/// Fast 16-bit approximation of sin(x). This approximation never varies more than
+/// 0.69% from the floating point value you'd get by doing
+///
+///     float s = sin(x) * 32767.0;
+///
+/// @param theta input angle from 0-65535
+/// @returns sin of theta, value between -32767 to 32767.
+LIB8STATIC int16_t sin16_avr( uint16_t theta )
+{
+    static const uint8_t data[] =
+    { 0,         0,         49, 0, 6393%256,   6393/256, 48, 0,
+      12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,
+      23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,
+      30273%256, 30273/256, 14, 0, 32137%256, 32137/256,  4 /*,0*/ };
+
+    uint16_t offset = (theta & 0x3FFF);
+
+    // AVR doesn't have a multi-bit shift instruction,
+    // so if we say "offset >>= 3", gcc makes a tiny loop.
+    // Inserting empty volatile statements between each
+    // bit shift forces gcc to unroll the loop.
+    offset >>= 1; // 0..8191
+    asm volatile("");
+    offset >>= 1; // 0..4095
+    asm volatile("");
+    offset >>= 1; // 0..2047
+
+    if( theta & 0x4000 ) offset = 2047 - offset;
+
+    uint8_t sectionX4;
+    sectionX4 = offset / 256;
+    sectionX4 *= 4;
+
+    uint8_t m;
+
+    union {
+        uint16_t b;
+        struct {
+            uint8_t blo;
+            uint8_t bhi;
+        };
+    } u;
+
+    //in effect u.b = blo + (256 * bhi);
+    u.blo = data[ sectionX4 ];
+    u.bhi = data[ sectionX4 + 1];
+    m     = data[ sectionX4 + 2];
+
+    uint8_t secoffset8 = (uint8_t)(offset) / 2;
+
+    uint16_t mx = m * secoffset8;
+
+    int16_t  y  = mx + u.b;
+    if( theta & 0x8000 ) y = -y;
+
+    return y;
+}
+
+/// Fast 16-bit approximation of sin(x). This approximation never varies more than
+/// 0.69% from the floating point value you'd get by doing
+///
+///     float s = sin(x) * 32767.0;
+///
+/// @param theta input angle from 0-65535
+/// @returns sin of theta, value between -32767 to 32767.
+LIB8STATIC int16_t sin16_C( uint16_t theta )
+{
+    static const uint16_t base[] =
+    { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
+    static const uint8_t slope[] =
+    { 49, 48, 44, 38, 31, 23, 14, 4 };
+
+    uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
+    if( theta & 0x4000 ) offset = 2047 - offset;
+
+    uint8_t section = offset / 256; // 0..7
+    uint16_t b   = base[section];
+    uint8_t  m   = slope[section];
+
+    uint8_t secoffset8 = (uint8_t)(offset) / 2;
+
+    uint16_t mx = m * secoffset8;
+    int16_t  y  = mx + b;
+
+    if( theta & 0x8000 ) y = -y;
+
+    return y;
+}
+
+
+/// Fast 16-bit approximation of cos(x). This approximation never varies more than
+/// 0.69% from the floating point value you'd get by doing
+///
+///     float s = cos(x) * 32767.0;
+///
+/// @param theta input angle from 0-65535
+/// @returns sin of theta, value between -32767 to 32767.
+LIB8STATIC int16_t cos16( uint16_t theta)
+{
+    return sin16( theta + 16384);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+// sin8 & cos8
+//        Fast 8-bit approximations of sin(x) & cos(x).
+//        Input angle is an unsigned int from 0-255.
+//        Output is an unsigned int from 0 to 255.
+//
+//        This approximation can vary to to 2%
+//        from the floating point value you'd get by doing
+//          float s = (sin( x ) * 128.0) + 128;
+//
+//        Don't use this approximation for calculating the
+//        "real" trigonometric calculations, but it's great
+//        for art projects and LED displays.
+//
+//        On Arduino/AVR, this approximation is more than
+//        20X faster than floating point sin(x) and cos(x)
+
+#if defined(__AVR__) && !defined(LIB8_ATTINY)
+#define sin8 sin8_avr
+#else
+#define sin8 sin8_C
+#endif
+
+
+const uint8_t b_m16_interleave[] = { 0, 49, 49, 41, 90, 27, 117, 10 };
+
+/// Fast 8-bit approximation of sin(x). This approximation never varies more than
+/// 2% from the floating point value you'd get by doing
+///
+///     float s = (sin(x) * 128.0) + 128;
+///
+/// @param theta input angle from 0-255
+/// @returns sin of theta, value between 0 and 255
+LIB8STATIC uint8_t  sin8_avr( uint8_t theta)
+{
+    uint8_t offset = theta;
+
+    asm volatile(
+                 "sbrc %[theta],6         \n\t"
+                 "com  %[offset]           \n\t"
+                 : [theta] "+r" (theta), [offset] "+r" (offset)
+                 );
+
+    offset &= 0x3F; // 0..63
+
+    uint8_t secoffset  = offset & 0x0F; // 0..15
+    if( theta & 0x40) secoffset++;
+
+    uint8_t m16; uint8_t b;
+
+    uint8_t section = offset >> 4; // 0..3
+    uint8_t s2 = section * 2;
+
+    const uint8_t* p = b_m16_interleave;
+    p += s2;
+    b   = *p;
+    p++;
+    m16 = *p;
+
+    uint8_t mx;
+    uint8_t xr1;
+    asm volatile(
+                 "mul %[m16],%[secoffset]   \n\t"
+                 "mov %[mx],r0              \n\t"
+                 "mov %[xr1],r1             \n\t"
+                 "eor  r1, r1               \n\t"
+                 "swap %[mx]                \n\t"
+                 "andi %[mx],0x0F           \n\t"
+                 "swap %[xr1]               \n\t"
+                 "andi %[xr1], 0xF0         \n\t"
+                 "or   %[mx], %[xr1]        \n\t"
+                 : [mx] "=d" (mx), [xr1] "=d" (xr1)
+                 : [m16] "d" (m16), [secoffset] "d" (secoffset)
+                 );
+
+    int8_t y = mx + b;
+    if( theta & 0x80 ) y = -y;
+
+    y += 128;
+
+    return y;
+}
+
+
+/// Fast 8-bit approximation of sin(x). This approximation never varies more than
+/// 2% from the floating point value you'd get by doing
+///
+///     float s = (sin(x) * 128.0) + 128;
+///
+/// @param theta input angle from 0-255
+/// @returns sin of theta, value between 0 and 255
+LIB8STATIC uint8_t sin8_C( uint8_t theta)
+{
+    uint8_t offset = theta;
+    if( theta & 0x40 ) {
+        offset = (uint8_t)255 - offset;
+    }
+    offset &= 0x3F; // 0..63
+
+    uint8_t secoffset  = offset & 0x0F; // 0..15
+    if( theta & 0x40) secoffset++;
+
+    uint8_t section = offset >> 4; // 0..3
+    uint8_t s2 = section * 2;
+    const uint8_t* p = b_m16_interleave;
+    p += s2;
+    uint8_t b   =  *p;
+    p++;
+    uint8_t m16 =  *p;
+
+    uint8_t mx = (m16 * secoffset) >> 4;
+
+    int8_t y = mx + b;
+    if( theta & 0x80 ) y = -y;
+
+    y += 128;
+
+    return y;
+}
+
+/// Fast 8-bit approximation of cos(x). This approximation never varies more than
+/// 2% from the floating point value you'd get by doing
+///
+///     float s = (cos(x) * 128.0) + 128;
+///
+/// @param theta input angle from 0-255
+/// @returns sin of theta, value between 0 and 255
+LIB8STATIC uint8_t cos8( uint8_t theta)
+{
+    return sin8( theta + 64);
+}
+
+///@}
+#endif
index 8ede053e71193f3d8744877f615828ddf031fcd3..c49877592eceb255b82cbac6fd77aef7f26c4d1c 100644 (file)
@@ -78,9 +78,11 @@ RGB hsv_to_rgb( HSV hsv )
                        break;
        }
 
+#ifdef USE_CIE1931_CURVE
        rgb.r = pgm_read_byte( &CIE1931_CURVE[rgb.r] );
        rgb.g = pgm_read_byte( &CIE1931_CURVE[rgb.g] );
        rgb.b = pgm_read_byte( &CIE1931_CURVE[rgb.b] );
+#endif
 
        return rgb;
 }
index 8316d1f06a3a28d2221798c4d9d650892ab73f26..8c928441c5ae1ed6bcd5d3be4b2d46f2904f0406 100644 (file)
@@ -274,10 +274,10 @@ bool process_record_quantum(keyrecord_t *record) {
   #ifdef HAPTIC_ENABLE
     process_haptic(keycode, record) &&
   #endif //HAPTIC_ENABLE
-    process_record_kb(keycode, record) &&
-  #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_KEYPRESSES)
+  #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_KEYREACTIVE_ENABLED)
     process_rgb_matrix(keycode, record) &&
   #endif
+    process_record_kb(keycode, record) &&
   #if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)
     process_midi(keycode, record) &&
   #endif
@@ -1049,12 +1049,6 @@ void matrix_init_quantum() {
   matrix_init_kb();
 }
 
-uint8_t rgb_matrix_task_counter = 0;
-
-#ifndef RGB_MATRIX_SKIP_FRAMES
-  #define RGB_MATRIX_SKIP_FRAMES 1
-#endif
-
 void matrix_scan_quantum() {
   #if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE)
     matrix_scan_music();
@@ -1078,10 +1072,6 @@ void matrix_scan_quantum() {
 
   #ifdef RGB_MATRIX_ENABLE
     rgb_matrix_task();
-    if (rgb_matrix_task_counter == 0) {
-      rgb_matrix_update_pwm_buffers();
-    }
-    rgb_matrix_task_counter = ((rgb_matrix_task_counter + 1) % (RGB_MATRIX_SKIP_FRAMES + 1));
   #endif
 
   #ifdef ENCODER_ENABLE
index 56a97e3c7d5b61f44ca400b8d223e82c72e07ebf..0728e2431fe63bcf036eec6d7e8fd2472765d8cd 100644 (file)
 #include <string.h>
 #include <math.h>
 
-rgb_config_t rgb_matrix_config;
+#include "lib/lib8tion/lib8tion.h"
+
+#include "rgb_matrix_animations/solid_color_anim.h"
+#include "rgb_matrix_animations/alpha_mods_anim.h"
+#include "rgb_matrix_animations/dual_beacon_anim.h"
+#include "rgb_matrix_animations/gradient_up_down_anim.h"
+#include "rgb_matrix_animations/raindrops_anim.h"
+#include "rgb_matrix_animations/cycle_all_anim.h"
+#include "rgb_matrix_animations/cycle_left_right_anim.h"
+#include "rgb_matrix_animations/cycle_up_down_anim.h"
+#include "rgb_matrix_animations/rainbow_beacon_anim.h"
+#include "rgb_matrix_animations/rainbow_pinwheels_anim.h"
+#include "rgb_matrix_animations/rainbow_moving_chevron_anim.h"
+#include "rgb_matrix_animations/jellybean_raindrops_anim.h"
+#include "rgb_matrix_animations/digital_rain_anim.h"
+#include "rgb_matrix_animations/solid_reactive_simple_anim.h"
+#include "rgb_matrix_animations/solid_reactive_anim.h"
+#include "rgb_matrix_animations/splash_anim.h"
+#include "rgb_matrix_animations/solid_splash_anim.h"
+#include "rgb_matrix_animations/breathing_anim.h"
 
-#ifndef MAX
-    #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+#ifndef RGB_DISABLE_AFTER_TIMEOUT
+  #define RGB_DISABLE_AFTER_TIMEOUT 0
 #endif
 
-#ifndef MIN
-    #define MIN(a,b) ((a) < (b)? (a): (b))
+#ifndef RGB_DISABLE_WHEN_USB_SUSPENDED
+  #define RGB_DISABLE_WHEN_USB_SUSPENDED false
 #endif
 
-#ifndef RGB_DISABLE_AFTER_TIMEOUT
-    #define RGB_DISABLE_AFTER_TIMEOUT 0
+#ifndef EECONFIG_RGB_MATRIX
+  #define EECONFIG_RGB_MATRIX EECONFIG_RGBLIGHT
 #endif
 
-#ifndef RGB_DISABLE_WHEN_USB_SUSPENDED
-    #define RGB_DISABLE_WHEN_USB_SUSPENDED false
+#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
+  #undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
+  #define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
 #endif
 
-#ifndef EECONFIG_RGB_MATRIX
-    #define EECONFIG_RGB_MATRIX EECONFIG_RGBLIGHT
+#if !defined(RGB_MATRIX_HUE_STEP)
+  #define RGB_MATRIX_HUE_STEP 8
 #endif
 
-#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > 255
-    #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 255
+#if !defined(RGB_MATRIX_SAT_STEP)
+  #define RGB_MATRIX_SAT_STEP 16
 #endif
 
-#ifndef RGB_DIGITAL_RAIN_DROPS
-    // lower the number for denser effect/wider keyboard
-    #define RGB_DIGITAL_RAIN_DROPS 24
+#if !defined(RGB_MATRIX_VAL_STEP)
+  #define RGB_MATRIX_VAL_STEP 16
 #endif
 
-#if !defined(DISABLE_RGB_MATRIX_RAINDROPS) || !defined(DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS) || !defined(DISABLE_RGB_MATRIX_DIGITAL_RAIN)
-    #define TRACK_PREVIOUS_EFFECT
+#if !defined(RGB_MATRIX_SPD_STEP)
+  #define RGB_MATRIX_SPD_STEP 16
 #endif
 
 bool g_suspend_state = false;
 
-// Global tick at 20 Hz
-uint32_t g_tick = 0;
-
-// Ticks since this key was last hit.
-uint8_t g_key_hit[DRIVER_LED_TOTAL];
+rgb_config_t rgb_matrix_config;
 
-// Ticks since any key was last hit.
-uint32_t g_any_key_hit = 0;
+rgb_counters_t g_rgb_counters;
+static uint32_t rgb_counters_buffer;
 
-#ifndef PI
-#define PI 3.14159265
-#endif
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+  last_hit_t g_last_hit_tracker;
+  static last_hit_t last_hit_buffer;
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
 
 uint32_t eeconfig_read_rgb_matrix(void) {
   return eeprom_read_dword(EECONFIG_RGB_MATRIX);
 }
+
 void eeconfig_update_rgb_matrix(uint32_t val) {
   eeprom_update_dword(EECONFIG_RGB_MATRIX, val);
 }
+
 void eeconfig_update_rgb_matrix_default(void) {
   dprintf("eeconfig_update_rgb_matrix_default\n");
   rgb_matrix_config.enable = 1;
@@ -90,11 +108,12 @@ void eeconfig_update_rgb_matrix_default(void) {
   rgb_matrix_config.mode = RGB_MATRIX_SOLID_COLOR;
 #endif
   rgb_matrix_config.hue = 0;
-  rgb_matrix_config.sat = 255;
+  rgb_matrix_config.sat = UINT8_MAX;
   rgb_matrix_config.val = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
-  rgb_matrix_config.speed = 0;
+  rgb_matrix_config.speed = UINT8_MAX / 2;
   eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
+
 void eeconfig_debug_rgb_matrix(void) {
   dprintf("rgb_matrix_config eprom\n");
   dprintf("rgb_matrix_config.enable = %d\n", rgb_matrix_config.enable);
@@ -105,710 +124,330 @@ void eeconfig_debug_rgb_matrix(void) {
   dprintf("rgb_matrix_config.speed = %d\n", rgb_matrix_config.speed);
 }
 
-// Last led hit
-#define LED_HITS_TO_REMEMBER 8
-uint8_t g_last_led_hit[LED_HITS_TO_REMEMBER] = {255};
-uint8_t g_last_led_count = 0;
-
-void map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i, uint8_t *led_count) {
-    rgb_led led;
-    *led_count = 0;
-
-    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        // map_index_to_led(i, &led);
-        led = g_rgb_leds[i];
-        if (row == led.matrix_co.row && column == led.matrix_co.col) {
-            led_i[*led_count] = i;
-            (*led_count)++;
-        }
+uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) {
+  // TODO: This is kinda expensive, fix this soonish
+  uint8_t led_count = 0;
+  for (uint8_t i = 0; i < DRIVER_LED_TOTAL && led_count < LED_HITS_TO_REMEMBER; i++) {
+    matrix_co_t matrix_co = g_rgb_leds[i].matrix_co;
+    if (row == matrix_co.row && column == matrix_co.col) {
+      led_i[led_count] = i;
+      led_count++;
     }
+  }
+  return led_count;
 }
 
 void rgb_matrix_update_pwm_buffers(void) {
-    rgb_matrix_driver.flush();
+  rgb_matrix_driver.flush();
 }
 
 void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) {
-    rgb_matrix_driver.set_color(index, red, green, blue);
+  rgb_matrix_driver.set_color(index, red, green, blue);
 }
 
 void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) {
-    rgb_matrix_driver.set_color_all(red, green, blue);
+  rgb_matrix_driver.set_color_all(red, green, blue);
 }
 
 bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
-    if ( record->event.pressed ) {
-        uint8_t led[8], led_count;
-        map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count);
-        if (led_count > 0) {
-            for (uint8_t i = LED_HITS_TO_REMEMBER; i > 1; i--) {
-                g_last_led_hit[i - 1] = g_last_led_hit[i - 2];
-            }
-            g_last_led_hit[0] = led[0];
-            g_last_led_count = MIN(LED_HITS_TO_REMEMBER, g_last_led_count + 1);
-        }
-        for(uint8_t i = 0; i < led_count; i++)
-            g_key_hit[led[i]] = 0;
-        g_any_key_hit = 0;
-    } else {
-        #ifdef RGB_MATRIX_KEYRELEASES
-        uint8_t led[8], led_count;
-        map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count);
-        for(uint8_t i = 0; i < led_count; i++)
-            g_key_hit[led[i]] = 255;
-
-        g_any_key_hit = 255;
-        #endif
-    }
-    return true;
-}
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+  uint8_t led[LED_HITS_TO_REMEMBER];
+  uint8_t led_count = 0;
+
+#if defined(RGB_MATRIX_KEYRELEASES)
+  if (!record->event.pressed) {
+    led_count = rgb_matrix_map_row_column_to_led(record->event.key.row, record->event.key.col, led);
+    g_rgb_counters.any_key_hit = 0;
+  }
+#elif defined(RGB_MATRIX_KEYPRESSES)
+  if (record->event.pressed) {
+    led_count = rgb_matrix_map_row_column_to_led(record->event.key.row, record->event.key.col, led);
+    g_rgb_counters.any_key_hit = 0;
+  }
+#endif // defined(RGB_MATRIX_KEYRELEASES)
+
+  if (last_hit_buffer.count + led_count > LED_HITS_TO_REMEMBER) {
+    memcpy(&last_hit_buffer.x[0], &last_hit_buffer.x[led_count], LED_HITS_TO_REMEMBER - led_count);
+    memcpy(&last_hit_buffer.y[0], &last_hit_buffer.y[led_count], LED_HITS_TO_REMEMBER - led_count);
+    memcpy(&last_hit_buffer.tick[0], &last_hit_buffer.tick[led_count], (LED_HITS_TO_REMEMBER - led_count) * 2); // 16 bit
+    memcpy(&last_hit_buffer.index[0], &last_hit_buffer.index[led_count], LED_HITS_TO_REMEMBER - led_count);
+    last_hit_buffer.count--;
+  }
 
-void rgb_matrix_set_suspend_state(bool state) {
-    g_suspend_state = state;
+  for(uint8_t i = 0; i < led_count; i++) {
+    uint8_t index = last_hit_buffer.count;
+    last_hit_buffer.x[index] = g_rgb_leds[led[i]].point.x;
+    last_hit_buffer.y[index] = g_rgb_leds[led[i]].point.y;
+    last_hit_buffer.index[index] = led[i];
+    last_hit_buffer.tick[index] = 0;
+    last_hit_buffer.count++;
+  }
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+  return true;
 }
 
 void rgb_matrix_test(void) {
-    // Mask out bits 4 and 5
-    // Increase the factor to make the test animation slower (and reduce to make it faster)
-    uint8_t factor = 10;
-    switch ( (g_tick & (0b11 << factor)) >> factor )
-    {
-        case 0:
-        {
-            rgb_matrix_set_color_all( 20, 0, 0 );
-            break;
-        }
-        case 1:
-        {
-            rgb_matrix_set_color_all( 0, 20, 0 );
-            break;
-        }
-        case 2:
-        {
-            rgb_matrix_set_color_all( 0, 0, 20 );
-            break;
-        }
-        case 3:
-        {
-            rgb_matrix_set_color_all( 20, 20, 20 );
-            break;
-        }
+  // Mask out bits 4 and 5
+  // Increase the factor to make the test animation slower (and reduce to make it faster)
+  uint8_t factor = 10;
+  switch ( (g_rgb_counters.tick & (0b11 << factor)) >> factor )
+  {
+    case 0: {
+      rgb_matrix_set_color_all( 20, 0, 0 );
+      break;
     }
-}
-
-// All LEDs off
-void rgb_matrix_all_off(void) {
-    rgb_matrix_set_color_all( 0, 0, 0 );
-}
-
-// Solid color
-void rgb_matrix_solid_color(void) {
-    HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
-    RGB rgb = hsv_to_rgb( hsv );
-    rgb_matrix_set_color_all( rgb.r, rgb.g, rgb.b );
-}
-
-void rgb_matrix_solid_reactive(void) {
-       // Relies on hue being 8-bit and wrapping
-       for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-       {
-               uint16_t offset2 = g_key_hit[i]<<2;
-               offset2 = (offset2<=130) ? (130-offset2) : 0;
-
-               HSV hsv = { .h = rgb_matrix_config.hue+offset2, .s = 255, .v = rgb_matrix_config.val };
-               RGB rgb = hsv_to_rgb( hsv );
-               rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-       }
-}
-
-void rgb_matrix_solid_reactive_simple(void)
-{
-    HSV hsv = {.h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val};
-    RGB rgb;
-    
-    for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
-        uint16_t offset2 = g_key_hit[i] << 2;
-        offset2 = (offset2 <= 255) ? (255 - offset2) : 0;
-        hsv.v = offset2 * rgb_matrix_config.val / RGB_MATRIX_MAXIMUM_BRIGHTNESS;
-        rgb = hsv_to_rgb(hsv);
-        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
-    }
-}
-
-// alphas = color1, mods = color2
-void rgb_matrix_alphas_mods(void) {
-
-    RGB rgb1 = hsv_to_rgb( (HSV){ .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val } );
-    RGB rgb2 = hsv_to_rgb( (HSV){ .h = (rgb_matrix_config.hue + 180) % 360, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val } );
-
-    rgb_led led;
-    for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
-        led = g_rgb_leds[i];
-        if ( led.matrix_co.raw < 0xFF ) {
-            if ( led.modifier )
-            {
-                rgb_matrix_set_color( i, rgb2.r, rgb2.g, rgb2.b );
-            }
-            else
-            {
-                rgb_matrix_set_color( i, rgb1.r, rgb1.g, rgb1.b );
-            }
-        }
-    }
-}
-
-void rgb_matrix_gradient_up_down(void) {
-    int16_t h1 = rgb_matrix_config.hue;
-    int16_t h2 = (rgb_matrix_config.hue + 180) % 360;
-    int16_t deltaH = h2 - h1;
-
-    // Take the shortest path between hues
-    if ( deltaH > 127 )
-    {
-        deltaH -= 256;
-    }
-    else if ( deltaH < -127 )
-    {
-        deltaH += 256;
-    }
-    // Divide delta by 4, this gives the delta per row
-    deltaH /= 4;
-
-    int16_t s1 = rgb_matrix_config.sat;
-    int16_t s2 = rgb_matrix_config.hue;
-    int16_t deltaS = ( s2 - s1 ) / 4;
-
-    HSV hsv = { .h = 0, .s = 255, .v = rgb_matrix_config.val };
-    RGB rgb;
-    Point point;
-    for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-    {
-        // map_led_to_point( i, &point );
-        point = g_rgb_leds[i].point;
-        // The y range will be 0..64, map this to 0..4
-        uint8_t y = (point.y>>4);
-        // Relies on hue being 8-bit and wrapping
-        hsv.h = rgb_matrix_config.hue + ( deltaH * y );
-        hsv.s = rgb_matrix_config.sat + ( deltaS * y );
-        rgb = hsv_to_rgb( hsv );
-        rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-    }
-}
-
-void rgb_matrix_raindrops(bool initialize) {
-    int16_t h1 = rgb_matrix_config.hue;
-    int16_t h2 = (rgb_matrix_config.hue + 180) % 360;
-    int16_t deltaH = h2 - h1;
-    deltaH /= 4;
-
-    // Take the shortest path between hues
-    if ( deltaH > 127 )
-    {
-        deltaH -= 256;
+    case 1: {
+      rgb_matrix_set_color_all( 0, 20, 0 );
+      break;
     }
-    else if ( deltaH < -127 )
-    {
-        deltaH += 256;
+    case 2: {
+      rgb_matrix_set_color_all( 0, 0, 20 );
+      break;
     }
-
-    int16_t s1 = rgb_matrix_config.sat;
-    int16_t s2 = rgb_matrix_config.sat;
-    int16_t deltaS = ( s2 - s1 ) / 4;
-
-    HSV hsv;
-    RGB rgb;
-
-    // Change one LED every tick, make sure speed is not 0
-    uint8_t led_to_change = ( g_tick & ( 0x0A / (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed) ) ) == 0 ? rand() % (DRIVER_LED_TOTAL) : 255;
-
-    for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-    {
-        // If initialize, all get set to random colors
-        // If not, all but one will stay the same as before.
-        if ( initialize || i == led_to_change )
-        {
-            hsv.h = h1 + ( deltaH * ( rand() & 0x03 ) );
-            hsv.s = s1 + ( deltaS * ( rand() & 0x03 ) );
-            // Override brightness with global brightness control
-            hsv.v = rgb_matrix_config.val;
-
-            rgb = hsv_to_rgb( hsv );
-            rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-        }
-    }
-}
-
-void rgb_matrix_cycle_all(void) {
-    uint8_t offset = ( g_tick << rgb_matrix_config.speed ) & 0xFF;
-
-    rgb_led led;
-
-    // Relies on hue being 8-bit and wrapping
-    for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-    {
-        // map_index_to_led(i, &led);
-        led = g_rgb_leds[i];
-        if (led.matrix_co.raw < 0xFF) {
-            uint16_t offset2 = g_key_hit[i]<<2;
-            offset2 = (offset2<=63) ? (63-offset2) : 0;
-
-            HSV hsv = { .h = offset+offset2, .s = 255, .v = rgb_matrix_config.val };
-            RGB rgb = hsv_to_rgb( hsv );
-            rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-        }
-    }
-}
-
-void rgb_matrix_cycle_left_right(void) {
-    uint8_t offset = ( g_tick << rgb_matrix_config.speed ) & 0xFF;
-    HSV hsv = { .h = 0, .s = 255, .v = rgb_matrix_config.val };
-    RGB rgb;
-    Point point;
-    rgb_led led;
-    for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-    {
-        // map_index_to_led(i, &led);
-        led = g_rgb_leds[i];
-        if (led.matrix_co.raw < 0xFF) {
-            uint16_t offset2 = g_key_hit[i]<<2;
-            offset2 = (offset2<=63) ? (63-offset2) : 0;
-
-            // map_led_to_point( i, &point );
-            point = g_rgb_leds[i].point;
-            // Relies on hue being 8-bit and wrapping
-            hsv.h = point.x + offset + offset2;
-            rgb = hsv_to_rgb( hsv );
-            rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-        }
-    }
-}
-
-void rgb_matrix_cycle_up_down(void) {
-    uint8_t offset = ( g_tick << rgb_matrix_config.speed ) & 0xFF;
-    HSV hsv = { .h = 0, .s = 255, .v = rgb_matrix_config.val };
-    RGB rgb;
-    Point point;
-    rgb_led led;
-    for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-    {
-        // map_index_to_led(i, &led);
-        led = g_rgb_leds[i];
-        if (led.matrix_co.raw < 0xFF) {
-            uint16_t offset2 = g_key_hit[i]<<2;
-            offset2 = (offset2<=63) ? (63-offset2) : 0;
-
-            // map_led_to_point( i, &point );
-            point = g_rgb_leds[i].point;
-            // Relies on hue being 8-bit and wrapping
-            hsv.h = point.y + offset + offset2;
-            rgb = hsv_to_rgb( hsv );
-            rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-        }
+    case 3: {
+      rgb_matrix_set_color_all( 20, 20, 20 );
+      break;
     }
+  }
 }
 
+static bool rgb_matrix_none(effect_params_t* params) {
+  if (!params->init) {
+    return false;
+  }
 
-void rgb_matrix_dual_beacon(void) {
-    HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
-    RGB rgb;
-    Point point;
-    double cos_value = cos(g_tick * PI / 128) / 32;
-    double sin_value =  sin(g_tick * PI / 128) / 112;
-    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        point = g_rgb_leds[i].point;
-        hsv.h = ((point.y - 32.0)* cos_value + (point.x - 112.0) * sin_value) * (180) + rgb_matrix_config.hue;
-        rgb = hsv_to_rgb( hsv );
-        rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-    }
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    rgb_matrix_set_color(i, 0, 0, 0);
+  }
+  return led_max < DRIVER_LED_TOTAL;
 }
 
-void rgb_matrix_rainbow_beacon(void) {
-    HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
-    RGB rgb;
-    Point point;
-    double cos_value = cos(g_tick * PI / 128);
-    double sin_value =  sin(g_tick * PI / 128);
-    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        point = g_rgb_leds[i].point;
-        hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.y - 32.0)* cos_value + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.x - 112.0) * sin_value + rgb_matrix_config.hue;
-        rgb = hsv_to_rgb( hsv );
-        rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-    }
-}
+static uint8_t rgb_last_enable = UINT8_MAX;
+static uint8_t rgb_last_effect = UINT8_MAX;
+static effect_params_t rgb_effect_params = { 0, 0 };
+static rgb_task_states rgb_task_state = SYNCING;
 
-void rgb_matrix_rainbow_pinwheels(void) {
-    HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
-    RGB rgb;
-    Point point;
-    double cos_value = cos(g_tick * PI / 128);
-    double sin_value =  sin(g_tick * PI / 128);
-    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        point = g_rgb_leds[i].point;
-        hsv.h = (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.y - 32.0)* cos_value + (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (66 - abs(point.x - 112.0)) * sin_value + rgb_matrix_config.hue;
-        rgb = hsv_to_rgb( hsv );
-        rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
+static void rgb_task_timers(void) {
+  // Update double buffer timers
+  uint16_t deltaTime = timer_elapsed32(rgb_counters_buffer);
+  rgb_counters_buffer = timer_read32();
+  if (g_rgb_counters.any_key_hit < UINT32_MAX) {
+    if (UINT32_MAX - deltaTime < g_rgb_counters.any_key_hit) {
+      g_rgb_counters.any_key_hit = UINT32_MAX;
+    } else {
+      g_rgb_counters.any_key_hit += deltaTime;
     }
-}
+  }
 
-void rgb_matrix_rainbow_moving_chevron(void) {
-    HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
-    RGB rgb;
-    Point point;
-    uint8_t r = 128;
-    double cos_value = cos(r * PI / 128);
-    double sin_value =  sin(r * PI / 128);
-    double multiplier = (g_tick / 256.0 * 224);
-    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        point = g_rgb_leds[i].point;
-        hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * abs(point.y - 32.0)* sin_value + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.x - multiplier) * cos_value + rgb_matrix_config.hue;
-        rgb = hsv_to_rgb( hsv );
-        rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
+  // Update double buffer last hit timers
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+  uint8_t count = last_hit_buffer.count;
+  for (uint8_t i = 0; i < count; ++i) {
+    if (UINT16_MAX - deltaTime < last_hit_buffer.tick[i]) {
+      last_hit_buffer.count--;
+      continue;
     }
-}
-
-
-void rgb_matrix_jellybean_raindrops( bool initialize ) {
-    HSV hsv;
-    RGB rgb;
+    last_hit_buffer.tick[i] += deltaTime;
+  }
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+}
+
+static void rgb_task_sync(void) {
+  // next task
+  if (timer_elapsed32(g_rgb_counters.tick) >= RGB_MATRIX_LED_FLUSH_LIMIT)
+    rgb_task_state = STARTING;
+}
+
+static void rgb_task_start(void) {
+  // reset iter
+  rgb_effect_params.iter = 0;
+
+  // update double buffers
+  g_rgb_counters.tick = rgb_counters_buffer;
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+  g_last_hit_tracker = last_hit_buffer;
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+
+  // next task
+  rgb_task_state = RENDERING;
+}
+
+static void rgb_task_render(uint8_t effect) {
+  bool rendering = false;
+  rgb_effect_params.init = (effect != rgb_last_effect) || (rgb_matrix_config.enable != rgb_last_enable);
+
+  // each effect can opt to do calculations
+  // and/or request PWM buffer updates.
+  switch (effect) {
+    case RGB_MATRIX_NONE:
+      rendering = rgb_matrix_none(&rgb_effect_params);
+      break;
+
+    case RGB_MATRIX_SOLID_COLOR:
+      rendering = rgb_matrix_solid_color(&rgb_effect_params);           // Max 1ms Avg 0ms
+      break;
+#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
+    case RGB_MATRIX_ALPHAS_MODS:
+      rendering = rgb_matrix_alphas_mods(&rgb_effect_params);           // Max 2ms Avg 1ms
+      break;
+#endif // DISABLE_RGB_MATRIX_ALPHAS_MODS
+#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+    case RGB_MATRIX_GRADIENT_UP_DOWN:
+      rendering = rgb_matrix_gradient_up_down(&rgb_effect_params);      // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+#ifndef DISABLE_RGB_MATRIX_BREATHING
+    case RGB_MATRIX_BREATHING:
+      rendering = rgb_matrix_breathing(&rgb_effect_params);             // Max 1ms Avg 0ms
+      break;
+#endif // DISABLE_RGB_MATRIX_BREATHING
+#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
+    case RGB_MATRIX_CYCLE_ALL:
+      rendering = rgb_matrix_cycle_all(&rgb_effect_params);             // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_CYCLE_ALL
+#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+    case RGB_MATRIX_CYCLE_LEFT_RIGHT:
+      rendering = rgb_matrix_cycle_left_right(&rgb_effect_params);      // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+    case RGB_MATRIX_CYCLE_UP_DOWN:
+      rendering = rgb_matrix_cycle_up_down(&rgb_effect_params);         // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+    case RGB_MATRIX_RAINBOW_MOVING_CHEVRON:
+      rendering = rgb_matrix_rainbow_moving_chevron(&rgb_effect_params); // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
+    case RGB_MATRIX_DUAL_BEACON:
+      rendering = rgb_matrix_dual_beacon(&rgb_effect_params);           // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_DUAL_BEACON
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
+    case RGB_MATRIX_RAINBOW_BEACON:
+      rendering = rgb_matrix_rainbow_beacon(&rgb_effect_params);        // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_RAINBOW_BEACON
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+    case RGB_MATRIX_RAINBOW_PINWHEELS:
+      rendering = rgb_matrix_rainbow_pinwheels(&rgb_effect_params);     // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+#ifndef DISABLE_RGB_MATRIX_RAINDROPS
+    case RGB_MATRIX_RAINDROPS:
+      rendering = rgb_matrix_raindrops(&rgb_effect_params);             // Max 1ms Avg 0ms
+      break;
+#endif // DISABLE_RGB_MATRIX_RAINDROPS
+#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+    case RGB_MATRIX_JELLYBEAN_RAINDROPS:
+      rendering = rgb_matrix_jellybean_raindrops(&rgb_effect_params);   // Max 1ms Avg 0ms
+      break;
+#endif // DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+#ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
+    case RGB_MATRIX_DIGITAL_RAIN:
+      rendering = rgb_matrix_digital_rain(&rgb_effect_params);         // Max 9ms Avg 8ms | this is expensive, fix it
+      break;
+#endif // DISABLE_RGB_MATRIX_DIGITAL_RAIN
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+    case RGB_MATRIX_SOLID_REACTIVE_SIMPLE:
+      rendering = rgb_matrix_solid_reactive_simple(&rgb_effect_params);// Max 4ms Avg 3ms
+      break;
+#endif
+#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
+    case RGB_MATRIX_SOLID_REACTIVE:
+      rendering = rgb_matrix_solid_reactive(&rgb_effect_params);       // Max 4ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE
+#ifndef DISABLE_RGB_MATRIX_SPLASH
+    case RGB_MATRIX_SPLASH:
+      rendering = rgb_matrix_splash(&rgb_effect_params);               // Max 5ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_SPLASH
+#ifndef DISABLE_RGB_MATRIX_MULTISPLASH
+    case RGB_MATRIX_MULTISPLASH:
+      rendering = rgb_matrix_multisplash(&rgb_effect_params);          // Max 10ms Avg 5ms
+      break;
+#endif // DISABLE_RGB_MATRIX_MULTISPLASH
+#ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
+    case RGB_MATRIX_SOLID_SPLASH:
+      rendering = rgb_matrix_solid_splash(&rgb_effect_params);         // Max 5ms Avg 3ms
+      break;
+#endif // DISABLE_RGB_MATRIX_SOLID_SPLASH
+#ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+    case RGB_MATRIX_SOLID_MULTISPLASH:
+      rendering = rgb_matrix_solid_multisplash(&rgb_effect_params);    // Max 10ms Avg 5ms
+      break;
+#endif // DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
 
-    // Change one LED every tick, make sure speed is not 0
-    uint8_t led_to_change = ( g_tick & ( 0x0A / (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed) ) ) == 0 ? rand() % (DRIVER_LED_TOTAL) : 255;
+    // Factory default magic value
+    case UINT8_MAX: {
+        rgb_matrix_test();
+        rgb_task_state = FLUSHING;
+      }
+      return;
+  }
 
-    for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-    {
-        // If initialize, all get set to random colors
-        // If not, all but one will stay the same as before.
-        if ( initialize || i == led_to_change )
-        {
-            hsv.h = rand() & 0xFF;
-            hsv.s = rand() & 0xFF;
-            // Override brightness with global brightness control
-            hsv.v = rgb_matrix_config.val;
+  rgb_effect_params.iter++;
 
-            rgb = hsv_to_rgb( hsv );
-            rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-        }
+  // next task
+  if (!rendering) {
+    rgb_task_state = FLUSHING;
+    if (!rgb_effect_params.init && effect == RGB_MATRIX_NONE) {
+      // We only need to flush once if we are RGB_MATRIX_NONE
+      rgb_task_state = SYNCING;
     }
+  }
 }
 
-void rgb_matrix_digital_rain( const bool initialize ) {
-    // algorithm ported from https://github.com/tremby/Kaleidoscope-LEDEffect-DigitalRain
-    const uint8_t drop_ticks           = 28;
-    const uint8_t pure_green_intensity = 0xd0;
-    const uint8_t max_brightness_boost = 0xc0;
-    const uint8_t max_intensity        = 0xff;
+static void rgb_task_flush(uint8_t effect) {
+  // update last trackers after the first full render so we can init over several frames
+  rgb_last_effect = effect;
+  rgb_last_enable = rgb_matrix_config.enable;
 
-    static uint8_t map[MATRIX_COLS][MATRIX_ROWS] = {{0}};
-    static uint8_t drop = 0;
-
-    if (initialize) {
-        rgb_matrix_set_color_all(0, 0, 0);
-        memset(map, 0, sizeof map);
-        drop = 0;
-    }
-    for (uint8_t col = 0; col < MATRIX_COLS; col++) {
-        for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
-            if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) {
-                // top row, pixels have just fallen and we're
-                // making a new rain drop in this column
-                map[col][row] = max_intensity;
-            }
-            else if (map[col][row] > 0 && map[col][row] < max_intensity) {
-                // neither fully bright nor dark, decay it
-                map[col][row]--;
-            }
-            // set the pixel colour
-            uint8_t led, led_count;
-            map_row_column_to_led(row, col, &led, &led_count);
-
-            if (map[col][row] > pure_green_intensity) {
-                const uint8_t boost = (uint8_t) ((uint16_t) max_brightness_boost
-                        * (map[col][row] - pure_green_intensity) / (max_intensity - pure_green_intensity));
-                rgb_matrix_set_color(led, boost, max_intensity, boost);
-            }
-            else {
-                const uint8_t green = (uint8_t) ((uint16_t) max_intensity * map[col][row] / pure_green_intensity);
-                rgb_matrix_set_color(led, 0, green, 0);
-            }
-        }
-    }
-    if (++drop > drop_ticks) {
-        // reset drop timer
-        drop = 0;
-        for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) {
-            for (uint8_t col = 0; col < MATRIX_COLS; col++) {
-                // if ths is on the bottom row and bright allow decay
-                if (row == MATRIX_ROWS - 1 && map[col][row] == max_intensity) {
-                    map[col][row]--;
-                }
-                // check if the pixel above is bright
-                if (map[col][row - 1] == max_intensity) {
-                    // allow old bright pixel to decay
-                    map[col][row - 1]--;
-                    // make this pixel bright
-                    map[col][row] = max_intensity;
-                }
-            }
-        }
-    }
-}
+  // update pwm buffers
+  rgb_matrix_update_pwm_buffers();
 
-void rgb_matrix_multisplash(void) {
-    // if (g_any_key_hit < 0xFF) {
-        HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
-        RGB rgb;
-        rgb_led led;
-        for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-            led = g_rgb_leds[i];
-            uint16_t c = 0, d = 0;
-            rgb_led last_led;
-            // if (g_last_led_count) {
-                for (uint8_t last_i = 0; last_i < g_last_led_count; last_i++) {
-                    last_led = g_rgb_leds[g_last_led_hit[last_i]];
-                    uint16_t dist = (uint16_t)sqrt(pow(led.point.x - last_led.point.x, 2) + pow(led.point.y - last_led.point.y, 2));
-                    uint16_t effect = (g_key_hit[g_last_led_hit[last_i]] << 2) - dist;
-                    c += MIN(MAX(effect, 0), 255);
-                    d += 255 - MIN(MAX(effect, 0), 255);
-                }
-            // } else {
-            //     d = 255;
-            // }
-            hsv.h = (rgb_matrix_config.hue + c) % 256;
-            hsv.v = MAX(MIN(d, 255), 0);
-            rgb = hsv_to_rgb( hsv );
-            rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-        }
-    // } else {
-        // rgb_matrix_set_color_all( 0, 0, 0 );
-    // }
-}
-
-
-void rgb_matrix_splash(void) {
-    g_last_led_count = MIN(g_last_led_count, 1);
-    rgb_matrix_multisplash();
-}
-
-
-void rgb_matrix_solid_multisplash(void) {
-    // if (g_any_key_hit < 0xFF) {
-        HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
-        RGB rgb;
-        rgb_led led;
-        for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-            led = g_rgb_leds[i];
-            uint16_t d = 0;
-            rgb_led last_led;
-            // if (g_last_led_count) {
-                for (uint8_t last_i = 0; last_i < g_last_led_count; last_i++) {
-                    last_led = g_rgb_leds[g_last_led_hit[last_i]];
-                    uint16_t dist = (uint16_t)sqrt(pow(led.point.x - last_led.point.x, 2) + pow(led.point.y - last_led.point.y, 2));
-                    uint16_t effect = (g_key_hit[g_last_led_hit[last_i]] << 2) - dist;
-                    d += 255 - MIN(MAX(effect, 0), 255);
-                }
-            // } else {
-            //     d = 255;
-            // }
-            hsv.v = MAX(MIN(d, 255), 0);
-            rgb = hsv_to_rgb( hsv );
-            rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-        }
-    // } else {
-        // rgb_matrix_set_color_all( 0, 0, 0 );
-    // }
-}
-
-
-void rgb_matrix_solid_splash(void) {
-    g_last_led_count = MIN(g_last_led_count, 1);
-    rgb_matrix_solid_multisplash();
-}
-
-
-// Needs eeprom access that we don't have setup currently
-
-void rgb_matrix_custom(void) {
-//     HSV hsv;
-//     RGB rgb;
-//     for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-//     {
-//         backlight_get_key_color(i, &hsv);
-//         // Override brightness with global brightness control
-//         hsv.v = rgb_matrix_config.val;
-//         rgb = hsv_to_rgb( hsv );
-//         rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
-//     }
+  // next task
+  rgb_task_state = SYNCING;
 }
 
 void rgb_matrix_task(void) {
-  #ifdef TRACK_PREVIOUS_EFFECT
-      static uint8_t toggle_enable_last = 255;
-  #endif
-       if (!rgb_matrix_config.enable) {
-     rgb_matrix_all_off();
-     rgb_matrix_indicators();
-     #ifdef TRACK_PREVIOUS_EFFECT
-         toggle_enable_last = rgb_matrix_config.enable;
-     #endif
-     return;
-    }
-    // delay 1 second before driving LEDs or doing anything else
-    static uint8_t startup_tick = 0;
-    if ( startup_tick < 20 ) {
-        startup_tick++;
-        return;
-    }
-
-    g_tick++;
-
-    if ( g_any_key_hit < 0xFFFFFFFF ) {
-        g_any_key_hit++;
-    }
-
-    for ( int led = 0; led < DRIVER_LED_TOTAL; led++ ) {
-        if ( g_key_hit[led] < 255 ) {
-            if (g_key_hit[led] == 254)
-                g_last_led_count = MAX(g_last_led_count - 1, 0);
-            g_key_hit[led]++;
-        }
-    }
-
-    // Factory default magic value
-    if ( rgb_matrix_config.mode == 255 ) {
-        rgb_matrix_test();
-        return;
-    }
-
-    // Ideally we would also stop sending zeros to the LED driver PWM buffers
-    // while suspended and just do a software shutdown. This is a cheap hack for now.
-    bool suspend_backlight = ((g_suspend_state && RGB_DISABLE_WHEN_USB_SUSPENDED) ||
-            (RGB_DISABLE_AFTER_TIMEOUT > 0 && g_any_key_hit > RGB_DISABLE_AFTER_TIMEOUT * 60 * 20));
-    uint8_t effect = suspend_backlight ? 0 : rgb_matrix_config.mode;
-
-    #ifdef TRACK_PREVIOUS_EFFECT
-        // Keep track of the effect used last time,
-        // detect change in effect, so each effect can
-        // have an optional initialization.
-
-        static uint8_t effect_last = 255;
-        bool initialize = (effect != effect_last) || (rgb_matrix_config.enable != toggle_enable_last);
-        effect_last = effect;
-        toggle_enable_last = rgb_matrix_config.enable;
-    #endif
-
-    // this gets ticked at 20 Hz.
-    // each effect can opt to do calculations
-    // and/or request PWM buffer updates.
-    switch ( effect ) {
-        case RGB_MATRIX_SOLID_COLOR:
-            rgb_matrix_solid_color();
-            break;
-        #ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
-            case RGB_MATRIX_ALPHAS_MODS:
-                rgb_matrix_alphas_mods();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
-            case RGB_MATRIX_DUAL_BEACON:
-                rgb_matrix_dual_beacon();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
-            case RGB_MATRIX_GRADIENT_UP_DOWN:
-                rgb_matrix_gradient_up_down();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_RAINDROPS
-            case RGB_MATRIX_RAINDROPS:
-                rgb_matrix_raindrops( initialize );
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
-            case RGB_MATRIX_CYCLE_ALL:
-                rgb_matrix_cycle_all();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
-            case RGB_MATRIX_CYCLE_LEFT_RIGHT:
-                rgb_matrix_cycle_left_right();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
-            case RGB_MATRIX_CYCLE_UP_DOWN:
-                rgb_matrix_cycle_up_down();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
-            case RGB_MATRIX_RAINBOW_BEACON:
-                rgb_matrix_rainbow_beacon();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
-            case RGB_MATRIX_RAINBOW_PINWHEELS:
-                rgb_matrix_rainbow_pinwheels();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
-            case RGB_MATRIX_RAINBOW_MOVING_CHEVRON:
-                rgb_matrix_rainbow_moving_chevron();
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
-            case RGB_MATRIX_JELLYBEAN_RAINDROPS:
-                rgb_matrix_jellybean_raindrops( initialize );
-                break;
-        #endif
-        #ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
-            case RGB_MATRIX_DIGITAL_RAIN:
-                rgb_matrix_digital_rain( initialize );
-                break;
-        #endif
-        #ifdef RGB_MATRIX_KEYPRESSES
-            #ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
-                case RGB_MATRIX_SOLID_REACTIVE:
-                    rgb_matrix_solid_reactive();
-                    break;
-            #endif
-            #ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
-                case RGB_MATRIX_SOLID_REACTIVE_SIMPLE:
-                    rgb_matrix_solid_reactive_simple();
-                    break;
-            #endif
-            #ifndef DISABLE_RGB_MATRIX_SPLASH
-                case RGB_MATRIX_SPLASH:
-                    rgb_matrix_splash();
-                    break;
-            #endif
-            #ifndef DISABLE_RGB_MATRIX_MULTISPLASH
-                case RGB_MATRIX_MULTISPLASH:
-                    rgb_matrix_multisplash();
-                    break;
-            #endif
-            #ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
-                case RGB_MATRIX_SOLID_SPLASH:
-                    rgb_matrix_solid_splash();
-                    break;
-            #endif
-            #ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
-                case RGB_MATRIX_SOLID_MULTISPLASH:
-                    rgb_matrix_solid_multisplash();
-                    break;
-            #endif
-        #endif
-        default:
-            rgb_matrix_custom();
-            break;
-    }
-
-    if ( ! suspend_backlight ) {
-        rgb_matrix_indicators();
-    }
+  rgb_task_timers();
+
+  // Ideally we would also stop sending zeros to the LED driver PWM buffers
+  // while suspended and just do a software shutdown. This is a cheap hack for now.
+  bool suspend_backlight = ((g_suspend_state && RGB_DISABLE_WHEN_USB_SUSPENDED) || (RGB_DISABLE_AFTER_TIMEOUT > 0 && g_rgb_counters.any_key_hit > RGB_DISABLE_AFTER_TIMEOUT * 60 * 20));
+  uint8_t effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;
+
+  switch (rgb_task_state) {
+    case STARTING:
+      rgb_task_start();
+      break;
+    case RENDERING:
+      rgb_task_render(effect);
+      break;
+    case FLUSHING:
+      rgb_task_flush(effect);
+      break;
+    case SYNCING:
+      rgb_task_sync();
+      break;
+  }
 
+  if (!suspend_backlight) {
+    rgb_matrix_indicators();
+  }
 }
 
 void rgb_matrix_indicators(void) {
-    rgb_matrix_indicators_kb();
-    rgb_matrix_indicators_user();
+  rgb_matrix_indicators_kb();
+  rgb_matrix_indicators_user();
 }
 
 __attribute__((weak))
@@ -817,103 +456,54 @@ void rgb_matrix_indicators_kb(void) {}
 __attribute__((weak))
 void rgb_matrix_indicators_user(void) {}
 
-
-// void rgb_matrix_set_indicator_index( uint8_t *index, uint8_t row, uint8_t column )
-// {
-//  if ( row >= MATRIX_ROWS )
-//  {
-//      // Special value, 255=none, 254=all
-//      *index = row;
-//  }
-//  else
-//  {
-//      // This needs updated to something like
-//      // uint8_t led[8], led_count;
-//      // map_row_column_to_led(row,column,led,&led_count);
-//      // for(uint8_t i = 0; i < led_count; i++)
-//      map_row_column_to_led( row, column, index );
-//  }
-// }
-
 void rgb_matrix_init(void) {
   rgb_matrix_driver.init();
 
   // TODO: put the 1 second startup delay here?
 
-  // clear the key hits
-  for ( int led=0; led<DRIVER_LED_TOTAL; led++ ) {
-      g_key_hit[led] = 255;
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+  g_last_hit_tracker.count = 0;
+  for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {
+    g_last_hit_tracker.tick[i] = UINT16_MAX;
   }
 
+  last_hit_buffer.count = 0;
+  for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {
+    last_hit_buffer.tick[i] = UINT16_MAX;
+  }
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
 
   if (!eeconfig_is_enabled()) {
-      dprintf("rgb_matrix_init_drivers eeconfig is not enabled.\n");
-      eeconfig_init();
-      eeconfig_update_rgb_matrix_default();
+    dprintf("rgb_matrix_init_drivers eeconfig is not enabled.\n");
+    eeconfig_init();
+    eeconfig_update_rgb_matrix_default();
   }
+
   rgb_matrix_config.raw = eeconfig_read_rgb_matrix();
+  rgb_matrix_config.speed = UINT8_MAX / 2; //EECONFIG needs to be increased to support this
   if (!rgb_matrix_config.mode) {
-      dprintf("rgb_matrix_init_drivers rgb_matrix_config.mode = 0. Write default values to EEPROM.\n");
-      eeconfig_update_rgb_matrix_default();
-      rgb_matrix_config.raw = eeconfig_read_rgb_matrix();
+    dprintf("rgb_matrix_init_drivers rgb_matrix_config.mode = 0. Write default values to EEPROM.\n");
+    eeconfig_update_rgb_matrix_default();
+    rgb_matrix_config.raw = eeconfig_read_rgb_matrix();
   }
   eeconfig_debug_rgb_matrix(); // display current eeprom values
 }
 
-// Deals with the messy details of incrementing an integer
-static uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
-    int16_t new_value = value;
-    new_value += step;
-    return MIN( MAX( new_value, min ), max );
-}
-
-static uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
-    int16_t new_value = value;
-    new_value -= step;
-    return MIN( MAX( new_value, min ), max );
-}
-
-// void *backlight_get_custom_key_color_eeprom_address( uint8_t led )
-// {
-//     // 3 bytes per color
-//     return EECONFIG_RGB_MATRIX + ( led * 3 );
-// }
-
-// void backlight_get_key_color( uint8_t led, HSV *hsv )
-// {
-//     void *address = backlight_get_custom_key_color_eeprom_address( led );
-//     hsv->h = eeprom_read_byte(address);
-//     hsv->s = eeprom_read_byte(address+1);
-//     hsv->v = eeprom_read_byte(address+2);
-// }
-
-// void backlight_set_key_color( uint8_t row, uint8_t column, HSV hsv )
-// {
-//     uint8_t led[8], led_count;
-//     map_row_column_to_led(row,column,led,&led_count);
-//     for(uint8_t i = 0; i < led_count; i++) {
-//         if ( led[i] < DRIVER_LED_TOTAL )
-//         {
-//             void *address = backlight_get_custom_key_color_eeprom_address(led[i]);
-//             eeprom_update_byte(address, hsv.h);
-//             eeprom_update_byte(address+1, hsv.s);
-//             eeprom_update_byte(address+2, hsv.v);
-//         }
-//     }
-// }
-
-uint32_t rgb_matrix_get_tick(void) {
-    return g_tick;
+void rgb_matrix_set_suspend_state(bool state) {
+  g_suspend_state = state;
 }
 
 void rgb_matrix_toggle(void) {
-       rgb_matrix_config.enable ^= 1;
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.enable ^= 1;
+  if (!rgb_matrix_config.enable) {
+    rgb_task_state = STARTING;
+  }
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_enable(void) {
        rgb_matrix_config.enable = 1;
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_enable_noeeprom(void) {
@@ -922,7 +512,7 @@ void rgb_matrix_enable_noeeprom(void) {
 
 void rgb_matrix_disable(void) {
        rgb_matrix_config.enable = 0;
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_disable_noeeprom(void) {
@@ -930,76 +520,79 @@ void rgb_matrix_disable_noeeprom(void) {
 }
 
 void rgb_matrix_step(void) {
-    rgb_matrix_config.mode++;
-    if (rgb_matrix_config.mode >= RGB_MATRIX_EFFECT_MAX)
-        rgb_matrix_config.mode = 1;
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.mode++;
+  if (rgb_matrix_config.mode >= RGB_MATRIX_EFFECT_MAX)
+    rgb_matrix_config.mode = 1;
+  rgb_task_state = STARTING;
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_step_reverse(void) {
-    rgb_matrix_config.mode--;
-    if (rgb_matrix_config.mode < 1)
-        rgb_matrix_config.mode = RGB_MATRIX_EFFECT_MAX - 1;
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.mode--;
+  if (rgb_matrix_config.mode < 1)
+    rgb_matrix_config.mode = RGB_MATRIX_EFFECT_MAX - 1;
+  rgb_task_state = STARTING;
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_increase_hue(void) {
-    rgb_matrix_config.hue = increment( rgb_matrix_config.hue, 8, 0, 255 );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.hue += RGB_MATRIX_HUE_STEP;
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_decrease_hue(void) {
-    rgb_matrix_config.hue = decrement( rgb_matrix_config.hue, 8, 0, 255 );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.hue -= RGB_MATRIX_HUE_STEP;
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_increase_sat(void) {
-    rgb_matrix_config.sat = increment( rgb_matrix_config.sat, 8, 0, 255 );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.sat = qadd8(rgb_matrix_config.sat, RGB_MATRIX_SAT_STEP);
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_decrease_sat(void) {
-    rgb_matrix_config.sat = decrement( rgb_matrix_config.sat, 8, 0, 255 );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.sat = qsub8(rgb_matrix_config.sat, RGB_MATRIX_SAT_STEP);
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_increase_val(void) {
-    rgb_matrix_config.val = increment( rgb_matrix_config.val, 8, 0, RGB_MATRIX_MAXIMUM_BRIGHTNESS );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.val = qadd8(rgb_matrix_config.val, RGB_MATRIX_VAL_STEP);
+  if (rgb_matrix_config.val > RGB_MATRIX_MAXIMUM_BRIGHTNESS)
+    rgb_matrix_config.val = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_decrease_val(void) {
-    rgb_matrix_config.val = decrement( rgb_matrix_config.val, 8, 0, RGB_MATRIX_MAXIMUM_BRIGHTNESS );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.val = qsub8(rgb_matrix_config.val, RGB_MATRIX_VAL_STEP);
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_increase_speed(void) {
-    rgb_matrix_config.speed = increment( rgb_matrix_config.speed, 1, 0, 3 );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
+  rgb_matrix_config.speed = qadd8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP);
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
 }
 
 void rgb_matrix_decrease_speed(void) {
-    rgb_matrix_config.speed = decrement( rgb_matrix_config.speed, 1, 0, 3 );
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
+  rgb_matrix_config.speed = qsub8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP);
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
 }
 
 void rgb_matrix_mode(uint8_t mode) {
-    rgb_matrix_config.mode = mode;
-    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+  rgb_matrix_config.mode = mode;
+  rgb_task_state = STARTING;
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
 void rgb_matrix_mode_noeeprom(uint8_t mode) {
-    rgb_matrix_config.mode = mode;
+  rgb_matrix_config.mode = mode;
 }
 
 uint8_t rgb_matrix_get_mode(void) {
-    return rgb_matrix_config.mode;
+  return rgb_matrix_config.mode;
 }
 
 void rgb_matrix_sethsv(uint16_t hue, uint8_t sat, uint8_t val) {
-  rgb_matrix_config.hue = hue;
-  rgb_matrix_config.sat = sat;
-  rgb_matrix_config.val = val;
+  rgb_matrix_sethsv_noeeprom(hue, sat, val);
   eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
@@ -1007,4 +600,6 @@ void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) {
   rgb_matrix_config.hue = hue;
   rgb_matrix_config.sat = sat;
   rgb_matrix_config.val = val;
+  if (rgb_matrix_config.val > RGB_MATRIX_MAXIMUM_BRIGHTNESS)
+    rgb_matrix_config.val = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
 }
index e6acd2d4b5125e4fb9b8428de204cedf0843d08b..855ea03230f259eaba22bef6d6ae410da3545f71 100644 (file)
 
 #include <stdint.h>
 #include <stdbool.h>
+#include "rgb_matrix_types.h"
 #include "color.h"
 #include "quantum.h"
 
 #ifdef IS31FL3731
-    #include "is31fl3731.h"
+  #include "is31fl3731.h"
 #elif defined (IS31FL3733)
-    #include "is31fl3733.h"
+  #include "is31fl3733.h"
 #endif
 
-typedef struct Point {
-       uint8_t x;
-       uint8_t y;
-} __attribute__((packed)) Point;
+#ifndef RGB_MATRIX_LED_FLUSH_LIMIT
+  #define RGB_MATRIX_LED_FLUSH_LIMIT 16
+#endif
 
-typedef struct rgb_led {
-       union {
-               uint8_t raw;
-               struct {
-                       uint8_t row:4; // 16 max
-                       uint8_t col:4; // 16 max
-               };
-       } matrix_co;
-       Point point;
-       uint8_t modifier:1;
-} __attribute__((packed)) rgb_led;
+#ifndef RGB_MATRIX_LED_PROCESS_LIMIT
+  #define RGB_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5
+#endif
 
+#if defined(RGB_MATRIX_LED_PROCESS_LIMIT) && RGB_MATRIX_LED_PROCESS_LIMIT > 0 && RGB_MATRIX_LED_PROCESS_LIMIT < DRIVER_LED_TOTAL
+#define RGB_MATRIX_USE_LIMITS(min, max) uint8_t min = RGB_MATRIX_LED_PROCESS_LIMIT * params->iter; \
+  uint8_t max = min + RGB_MATRIX_LED_PROCESS_LIMIT; \
+  if (max > DRIVER_LED_TOTAL) \
+    max = DRIVER_LED_TOTAL;
+#else
+#define RGB_MATRIX_USE_LIMITS(min, max) uint8_t min = 0; \
+  uint8_t max = DRIVER_LED_TOTAL;
+#endif
 
 extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
 
@@ -56,79 +57,73 @@ typedef struct
        uint8_t index;
 } rgb_indicator;
 
-typedef union {
-  uint32_t raw;
-  struct {
-    bool     enable  :1;
-    uint8_t  mode    :6;
-    uint16_t hue     :9;
-    uint8_t  sat     :8;
-    uint8_t  val     :8;
-    uint8_t  speed   :8;//EECONFIG needs to be increased to support this
-  };
-} rgb_config_t;
-
 enum rgb_matrix_effects {
+  RGB_MATRIX_NONE = 0,
        RGB_MATRIX_SOLID_COLOR = 1,
 #ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
-    RGB_MATRIX_ALPHAS_MODS,
-#endif
-#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
-    RGB_MATRIX_DUAL_BEACON,
-#endif
+  RGB_MATRIX_ALPHAS_MODS,
+#endif // DISABLE_RGB_MATRIX_ALPHAS_MODS
 #ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
-    RGB_MATRIX_GRADIENT_UP_DOWN,
-#endif
-#ifndef DISABLE_RGB_MATRIX_RAINDROPS
-    RGB_MATRIX_RAINDROPS,
-#endif
+  RGB_MATRIX_GRADIENT_UP_DOWN,
+#endif // DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+#ifndef DISABLE_RGB_MATRIX_BREATHING
+  RGB_MATRIX_BREATHING,
+#endif // DISABLE_RGB_MATRIX_BREATHING
 #ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
-    RGB_MATRIX_CYCLE_ALL,
-#endif
+  RGB_MATRIX_CYCLE_ALL,
+#endif // DISABLE_RGB_MATRIX_CYCLE_ALL
 #ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
-    RGB_MATRIX_CYCLE_LEFT_RIGHT,
-#endif
+  RGB_MATRIX_CYCLE_LEFT_RIGHT,
+#endif // DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
 #ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
-    RGB_MATRIX_CYCLE_UP_DOWN,
-#endif
+  RGB_MATRIX_CYCLE_UP_DOWN,
+#endif // DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+  RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
+#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
+  RGB_MATRIX_DUAL_BEACON,
+#endif // DISABLE_RGB_MATRIX_DUAL_BEACON
 #ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
-    RGB_MATRIX_RAINBOW_BEACON,
-#endif
+  RGB_MATRIX_RAINBOW_BEACON,
+#endif // DISABLE_RGB_MATRIX_RAINBOW_BEACON
 #ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
-    RGB_MATRIX_RAINBOW_PINWHEELS,
-#endif
-#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
-    RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
-#endif
+  RGB_MATRIX_RAINBOW_PINWHEELS,
+#endif // DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+#ifndef DISABLE_RGB_MATRIX_RAINDROPS
+  RGB_MATRIX_RAINDROPS,
+#endif // DISABLE_RGB_MATRIX_RAINDROPS
 #ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
-    RGB_MATRIX_JELLYBEAN_RAINDROPS,
-#endif
+  RGB_MATRIX_JELLYBEAN_RAINDROPS,
+#endif // DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
 #ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
-    RGB_MATRIX_DIGITAL_RAIN,
-#endif
-#ifdef RGB_MATRIX_KEYPRESSES
-   #ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
-       RGB_MATRIX_SOLID_REACTIVE,
-   #endif
-   #ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
-       RGB_MATRIX_SOLID_REACTIVE_SIMPLE,
-   #endif
-   #ifndef DISABLE_RGB_MATRIX_SPLASH
-       RGB_MATRIX_SPLASH,
-   #endif
-   #ifndef DISABLE_RGB_MATRIX_MULTISPLASH
-       RGB_MATRIX_MULTISPLASH,
-   #endif
-   #ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
-       RGB_MATRIX_SOLID_SPLASH,
-   #endif
-   #ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
-       RGB_MATRIX_SOLID_MULTISPLASH,
-   #endif
-#endif
-    RGB_MATRIX_EFFECT_MAX
+  RGB_MATRIX_DIGITAL_RAIN,
+#endif // DISABLE_RGB_MATRIX_DIGITAL_RAIN
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+  RGB_MATRIX_SOLID_REACTIVE_SIMPLE,
+#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
+  RGB_MATRIX_SOLID_REACTIVE,
+#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE
+#ifndef DISABLE_RGB_MATRIX_SPLASH
+  RGB_MATRIX_SPLASH,
+#endif // DISABLE_RGB_MATRIX_SPLASH
+#ifndef DISABLE_RGB_MATRIX_MULTISPLASH
+  RGB_MATRIX_MULTISPLASH,
+#endif // DISABLE_RGB_MATRIX_MULTISPLASH
+#ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
+  RGB_MATRIX_SOLID_SPLASH,
+#endif // DISABLE_RGB_MATRIX_SOLID_SPLASH
+#ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+  RGB_MATRIX_SOLID_MULTISPLASH,
+#endif // DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+  RGB_MATRIX_EFFECT_MAX
 };
 
+uint8_t rgb_matrix_map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i);
+
 void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue );
 void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue );
 
@@ -162,8 +157,6 @@ void rgb_matrix_decrease(void);
 // void backlight_get_key_color( uint8_t led, HSV *hsv );
 // void backlight_set_key_color( uint8_t row, uint8_t column, HSV hsv );
 
-uint32_t rgb_matrix_get_tick(void);
-
 void rgb_matrix_toggle(void);
 void rgb_matrix_enable(void);
 void rgb_matrix_enable_noeeprom(void);
@@ -212,7 +205,6 @@ uint8_t rgb_matrix_get_mode(void);
 typedef struct {
     /* Perform any initialisation required for the other driver functions to work. */
     void (*init)(void);
-
     /* Set the colour of a single LED in the buffer. */
     void (*set_color)(int index, uint8_t r, uint8_t g, uint8_t b);
     /* Set the colour of all LEDS on the keyboard in the buffer. */
diff --git a/quantum/rgb_matrix_animations/alpha_mods_anim.h b/quantum/rgb_matrix_animations/alpha_mods_anim.h
new file mode 100644 (file)
index 0000000..cc1914d
--- /dev/null
@@ -0,0 +1,26 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
+
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+// alphas = color1, mods = color2
+bool rgb_matrix_alphas_mods(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, rgb_matrix_config.val };
+  RGB rgb1 = hsv_to_rgb(hsv);
+  hsv.h += rgb_matrix_config.speed;
+  RGB rgb2 = hsv_to_rgb(hsv);
+
+  for (uint8_t i = led_min; i < led_max; i++) {
+    if (g_rgb_leds[i].modifier) {
+      rgb_matrix_set_color(i, rgb2.r, rgb2.g, rgb2.b);
+    } else {
+      rgb_matrix_set_color(i, rgb1.r, rgb1.g, rgb1.b);
+    }
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_ALPHAS_MODS
diff --git a/quantum/rgb_matrix_animations/breathing_anim.h b/quantum/rgb_matrix_animations/breathing_anim.h
new file mode 100644 (file)
index 0000000..fb90b66
--- /dev/null
@@ -0,0 +1,19 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_BREATHING
+
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_breathing(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 8);
+  uint8_t val = scale8(abs8(sin8(time) - 128) * 2, rgb_matrix_config.val);
+  HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, val };
+  RGB rgb = hsv_to_rgb(hsv);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_BREATHING
diff --git a/quantum/rgb_matrix_animations/cycle_all_anim.h b/quantum/rgb_matrix_animations/cycle_all_anim.h
new file mode 100644 (file)
index 0000000..5c18cfa
--- /dev/null
@@ -0,0 +1,21 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_cycle_all(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    hsv.h = time;
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_CYCLE_ALL
diff --git a/quantum/rgb_matrix_animations/cycle_left_right_anim.h b/quantum/rgb_matrix_animations/cycle_left_right_anim.h
new file mode 100644 (file)
index 0000000..f519aeb
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_cycle_left_right(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    point_t point = g_rgb_leds[i].point;
+    hsv.h = point.x - time;
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
diff --git a/quantum/rgb_matrix_animations/cycle_up_down_anim.h b/quantum/rgb_matrix_animations/cycle_up_down_anim.h
new file mode 100644 (file)
index 0000000..8b91d89
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_cycle_up_down(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    point_t point = g_rgb_leds[i].point;
+    hsv.h = point.y - time;
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
diff --git a/quantum/rgb_matrix_animations/digital_rain_anim.h b/quantum/rgb_matrix_animations/digital_rain_anim.h
new file mode 100644 (file)
index 0000000..4ba3c1c
--- /dev/null
@@ -0,0 +1,74 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
+
+#ifndef RGB_DIGITAL_RAIN_DROPS
+    // lower the number for denser effect/wider keyboard
+    #define RGB_DIGITAL_RAIN_DROPS 24
+#endif
+
+bool rgb_matrix_digital_rain(effect_params_t* params) {
+  // algorithm ported from https://github.com/tremby/Kaleidoscope-LEDEffect-DigitalRain
+  const uint8_t drop_ticks           = 28;
+  const uint8_t pure_green_intensity = 0xd0;
+  const uint8_t max_brightness_boost = 0xc0;
+  const uint8_t max_intensity        = 0xff;
+
+  static uint8_t map[MATRIX_COLS][MATRIX_ROWS] = {{0}};
+  static uint8_t drop = 0;
+
+  if (params->init) {
+    rgb_matrix_set_color_all(0, 0, 0);
+    memset(map, 0, sizeof map);
+    drop = 0;
+  }
+  for (uint8_t col = 0; col < MATRIX_COLS; col++) {
+    for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+      if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) {
+        // top row, pixels have just fallen and we're
+        // making a new rain drop in this column
+        map[col][row] = max_intensity;
+      }
+      else if (map[col][row] > 0 && map[col][row] < max_intensity) {
+        // neither fully bright nor dark, decay it
+        map[col][row]--;
+      }
+      // set the pixel colour
+      uint8_t led[LED_HITS_TO_REMEMBER];
+      uint8_t led_count = rgb_matrix_map_row_column_to_led(row, col, led);
+
+      // TODO: multiple leds are supported mapped to the same row/column
+      if (led_count > 0) {
+        if (map[col][row] > pure_green_intensity) {
+          const uint8_t boost = (uint8_t) ((uint16_t) max_brightness_boost * (map[col][row] - pure_green_intensity) / (max_intensity - pure_green_intensity));
+          rgb_matrix_set_color(led[0], boost, max_intensity, boost);
+        }
+        else {
+          const uint8_t green = (uint8_t) ((uint16_t) max_intensity * map[col][row] / pure_green_intensity);
+          rgb_matrix_set_color(led[0], 0, green, 0);
+        }
+      }
+    }
+  }
+  if (++drop > drop_ticks) {
+    // reset drop timer
+    drop = 0;
+    for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) {
+      for (uint8_t col = 0; col < MATRIX_COLS; col++) {
+        // if ths is on the bottom row and bright allow decay
+        if (row == MATRIX_ROWS - 1 && map[col][row] == max_intensity) {
+          map[col][row]--;
+        }
+        // check if the pixel above is bright
+        if (map[col][row - 1] == max_intensity) {
+          // allow old bright pixel to decay
+          map[col][row - 1]--;
+          // make this pixel bright
+          map[col][row] = max_intensity;
+        }
+      }
+    }
+  }
+  return false;
+}
+
+#endif // DISABLE_RGB_MATRIX_DIGITAL_RAIN
diff --git a/quantum/rgb_matrix_animations/dual_beacon_anim.h b/quantum/rgb_matrix_animations/dual_beacon_anim.h
new file mode 100644 (file)
index 0000000..dda3157
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_dual_beacon(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
+  int8_t cos_value = cos8(time) - 128;
+  int8_t sin_value = sin8(time) - 128;
+  for (uint8_t i = led_min; i < led_max; i++) {
+    point_t point = g_rgb_leds[i].point;
+    hsv.h = ((point.y - 32) * cos_value + (point.x - 112) * sin_value) / 128 + rgb_matrix_config.hue;
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_DUAL_BEACON
diff --git a/quantum/rgb_matrix_animations/gradient_up_down_anim.h b/quantum/rgb_matrix_animations/gradient_up_down_anim.h
new file mode 100644 (file)
index 0000000..11498e2
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_gradient_up_down(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint8_t scale = scale8(64, rgb_matrix_config.speed);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    point_t point = g_rgb_leds[i].point;
+    // The y range will be 0..64, map this to 0..4
+    // Relies on hue being 8-bit and wrapping
+    hsv.h = rgb_matrix_config.hue + scale * (point.y >> 4);
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+#endif // DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
diff --git a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
new file mode 100644 (file)
index 0000000..01ff5c2
--- /dev/null
@@ -0,0 +1,30 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+static void jellybean_raindrops_set_color(int i) {
+  HSV hsv = { rand() & 0xFF , rand() & 0xFF, rgb_matrix_config.val };
+  RGB rgb = hsv_to_rgb(hsv);
+  rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+}
+
+bool rgb_matrix_jellybean_raindrops(effect_params_t* params) {
+  if (!params->init) {
+    // Change one LED every tick, make sure speed is not 0
+    if (scale16by8(g_rgb_counters.tick, qadd8(rgb_matrix_config.speed, 16)) % 5 == 0) {
+      jellybean_raindrops_set_color(rand() % DRIVER_LED_TOTAL);
+    }
+    return false;
+  }
+
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+  for (int i = led_min; i < led_max; i++) {
+    jellybean_raindrops_set_color(i);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
diff --git a/quantum/rgb_matrix_animations/rainbow_beacon_anim.h b/quantum/rgb_matrix_animations/rainbow_beacon_anim.h
new file mode 100644 (file)
index 0000000..3c15e64
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_rainbow_beacon(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
+  int16_t cos_value = 2 * (cos8(time) - 128);
+  int16_t sin_value = 2 * (sin8(time) - 128);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    point_t point = g_rgb_leds[i].point;
+    hsv.h = ((point.y - 32) * cos_value + (point.x - 112) * sin_value) / 128 + rgb_matrix_config.hue;
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_RAINBOW_BEACON
diff --git a/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h b/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h
new file mode 100644 (file)
index 0000000..0d11d52
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_rainbow_moving_chevron(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    point_t point = g_rgb_leds[i].point;
+    hsv.h = abs8(point.y - 32) + (point.x - time) + rgb_matrix_config.hue;
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
diff --git a/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h b/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h
new file mode 100644 (file)
index 0000000..d7cd42c
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+
+extern rgb_counters_t g_rgb_counters;
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_rainbow_pinwheels(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
+  uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
+  int16_t cos_value = 3 * (cos8(time) - 128);
+  int16_t sin_value = 3 * (sin8(time) - 128);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    point_t point = g_rgb_leds[i].point;
+    hsv.h = ((point.y - 32) * cos_value + (56 - abs8(point.x - 112)) * sin_value) / 128 + rgb_matrix_config.hue;
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
diff --git a/quantum/rgb_matrix_animations/raindrops_anim.h b/quantum/rgb_matrix_animations/raindrops_anim.h
new file mode 100644 (file)
index 0000000..fc72137
--- /dev/null
@@ -0,0 +1,40 @@
+#pragma once
+#ifndef DISABLE_RGB_MATRIX_RAINDROPS
+#include "rgb_matrix_types.h"
+
+extern rgb_counters_t g_rgb_counters;
+extern rgb_config_t rgb_matrix_config;
+
+static void raindrops_set_color(int i) {
+  HSV hsv = { 0 , rgb_matrix_config.sat, rgb_matrix_config.val };
+
+  // Take the shortest path between hues
+  int16_t deltaH = ((rgb_matrix_config.hue + 180) % 360 - rgb_matrix_config.hue) / 4;
+  if (deltaH > 127) {
+    deltaH -= 256;
+  } else if (deltaH < -127) {
+    deltaH += 256;
+  }
+
+  hsv.h = rgb_matrix_config.hue + (deltaH * (rand() & 0x03));
+  RGB rgb = hsv_to_rgb(hsv);
+  rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+}
+
+bool rgb_matrix_raindrops(effect_params_t* params) {
+  if (!params->init) {
+    // Change one LED every tick, make sure speed is not 0
+    if (scale16by8(g_rgb_counters.tick, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0) {
+      raindrops_set_color(rand() % DRIVER_LED_TOTAL);
+    }
+    return false;
+  }
+
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+  for (int i = led_min; i < led_max; i++) {
+    raindrops_set_color(i);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_RAINDROPS
diff --git a/quantum/rgb_matrix_animations/solid_color_anim.h b/quantum/rgb_matrix_animations/solid_color_anim.h
new file mode 100644 (file)
index 0000000..24a197b
--- /dev/null
@@ -0,0 +1,14 @@
+#pragma once
+
+extern rgb_config_t rgb_matrix_config;
+
+bool rgb_matrix_solid_color(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, rgb_matrix_config.val };
+  RGB rgb = hsv_to_rgb(hsv);
+  for (uint8_t i = led_min; i < led_max; i++) {
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
diff --git a/quantum/rgb_matrix_animations/solid_reactive_anim.h b/quantum/rgb_matrix_animations/solid_reactive_anim.h
new file mode 100644 (file)
index 0000000..220e542
--- /dev/null
@@ -0,0 +1,33 @@
+#pragma once
+#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED)
+#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
+
+extern rgb_config_t rgb_matrix_config;
+extern last_hit_t g_last_hit_tracker;
+
+bool rgb_matrix_solid_reactive(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { rgb_matrix_config.hue, 255, rgb_matrix_config.val };
+  // Max tick based on speed scale ensures results from scale16by8 with rgb_matrix_config.speed are no greater than 255
+  uint16_t max_tick = 65535 / rgb_matrix_config.speed;
+  // Relies on hue being 8-bit and wrapping
+  for (uint8_t i = led_min; i < led_max; i++) {
+    uint16_t tick = max_tick;
+    for(uint8_t j = 0; j < g_last_hit_tracker.count; j++) {
+      if (g_last_hit_tracker.index[j] == i && g_last_hit_tracker.tick[j] < tick) {
+        tick = g_last_hit_tracker.tick[j];
+        break;
+      }
+    }
+
+    uint16_t  offset = scale16by8(tick, rgb_matrix_config.speed);
+    hsv.h = rgb_matrix_config.hue + qsub8(130, offset);
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED)
diff --git a/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h b/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h
new file mode 100644 (file)
index 0000000..e84cd69
--- /dev/null
@@ -0,0 +1,32 @@
+#pragma once
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+
+extern rgb_config_t rgb_matrix_config;
+extern last_hit_t g_last_hit_tracker;
+
+bool rgb_matrix_solid_reactive_simple(effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, 0 };
+  // Max tick based on speed scale ensures results from scale16by8 with rgb_matrix_config.speed are no greater than 255
+  uint16_t max_tick = 65535 / rgb_matrix_config.speed;
+  for (uint8_t i = led_min; i < led_max; i++) {
+    uint16_t tick = max_tick;
+    for(uint8_t j = 0; j < g_last_hit_tracker.count; j++) {
+      if (g_last_hit_tracker.index[j] == i && g_last_hit_tracker.tick[j] < tick) {
+        tick = g_last_hit_tracker.tick[j];
+        break;
+      }
+    }
+
+    uint16_t  offset = scale16by8(tick, rgb_matrix_config.speed);
+    hsv.v = scale8(255 - offset, rgb_matrix_config.val);
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/rgb_matrix_animations/solid_splash_anim.h b/quantum/rgb_matrix_animations/solid_splash_anim.h
new file mode 100644 (file)
index 0000000..82ac055
--- /dev/null
@@ -0,0 +1,42 @@
+#pragma once
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+#if !defined(DISABLE_RGB_MATRIX_SOLID_SPLASH) || !defined(DISABLE_RGB_MATRIX_SOLID_MULTISPLASH)
+
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+extern last_hit_t g_last_hit_tracker;
+
+static bool rgb_matrix_solid_multisplash_range(uint8_t start, effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, 0 };
+  uint8_t count = g_last_hit_tracker.count;
+  for (uint8_t i = led_min; i < led_max; i++) {
+    hsv.v = 0;
+    point_t point = g_rgb_leds[i].point;
+    for (uint8_t j = start; j < count; j++) {
+      int16_t dx = point.x - g_last_hit_tracker.x[j];
+      int16_t dy = point.y - g_last_hit_tracker.y[j];
+      uint8_t dist = sqrt16(dx * dx + dy * dy);
+      uint16_t effect = scale16by8(g_last_hit_tracker.tick[j], rgb_matrix_config.speed) - dist;
+      if (effect > 255)
+        effect = 255;
+      hsv.v = qadd8(hsv.v, 255 - effect);
+    }
+    hsv.v = scale8(hsv.v, rgb_matrix_config.val);
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+bool rgb_matrix_solid_multisplash(effect_params_t* params) {
+  return rgb_matrix_solid_multisplash_range(0, params);
+}
+
+bool rgb_matrix_solid_splash(effect_params_t* params) {
+  return rgb_matrix_solid_multisplash_range(qsub8(g_last_hit_tracker.count, 1), params);
+}
+
+#endif // !defined(DISABLE_RGB_MATRIX_SPLASH) && !defined(DISABLE_RGB_MATRIX_MULTISPLASH)
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/rgb_matrix_animations/splash_anim.h b/quantum/rgb_matrix_animations/splash_anim.h
new file mode 100644 (file)
index 0000000..829d30e
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+#if !defined(DISABLE_RGB_MATRIX_SPLASH) || !defined(DISABLE_RGB_MATRIX_MULTISPLASH)
+
+extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
+extern rgb_config_t rgb_matrix_config;
+extern last_hit_t g_last_hit_tracker;
+
+static bool rgb_matrix_multisplash_range(uint8_t start, effect_params_t* params) {
+  RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+  HSV hsv = { 0, rgb_matrix_config.sat, 0 };
+  uint8_t count = g_last_hit_tracker.count;
+  for (uint8_t i = led_min; i < led_max; i++) {
+    hsv.h = rgb_matrix_config.hue;
+    hsv.v = 0;
+    point_t point = g_rgb_leds[i].point;
+    for (uint8_t j = start; j < count; j++) {
+      int16_t dx = point.x - g_last_hit_tracker.x[j];
+      int16_t dy = point.y - g_last_hit_tracker.y[j];
+      uint8_t dist = sqrt16(dx * dx + dy * dy);
+      uint16_t effect = scale16by8(g_last_hit_tracker.tick[j], rgb_matrix_config.speed) - dist;
+      if (effect > 255)
+        effect = 255;
+      hsv.h += effect;
+      hsv.v = qadd8(hsv.v, 255 - effect);
+    }
+    hsv.v = scale8(hsv.v, rgb_matrix_config.val);
+    RGB rgb = hsv_to_rgb(hsv);
+    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+  }
+  return led_max < DRIVER_LED_TOTAL;
+}
+
+bool rgb_matrix_multisplash(effect_params_t* params) {
+  return rgb_matrix_multisplash_range(0, params);
+}
+
+bool rgb_matrix_splash(effect_params_t* params) {
+  return rgb_matrix_multisplash_range(qsub8(g_last_hit_tracker.count, 1), params);
+}
+
+#endif // !defined(DISABLE_RGB_MATRIX_SPLASH) || !defined(DISABLE_RGB_MATRIX_MULTISPLASH)
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/rgb_matrix_types.h b/quantum/rgb_matrix_types.h
new file mode 100644 (file)
index 0000000..f7643d2
--- /dev/null
@@ -0,0 +1,90 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#if defined(__GNUC__)
+#define PACKED __attribute__ ((__packed__))
+#else
+#define PACKED
+#endif
+
+#if defined(_MSC_VER)
+#pragma pack( push, 1 )
+#endif
+
+#if defined(RGB_MATRIX_KEYPRESSES) || defined(RGB_MATRIX_KEYRELEASES)
+  #define RGB_MATRIX_KEYREACTIVE_ENABLED
+#endif
+
+// Last led hit
+#ifndef LED_HITS_TO_REMEMBER
+  #define LED_HITS_TO_REMEMBER 8
+#endif // LED_HITS_TO_REMEMBER
+
+#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
+typedef struct PACKED {
+  uint8_t count;
+  uint8_t x[LED_HITS_TO_REMEMBER];
+  uint8_t y[LED_HITS_TO_REMEMBER];
+  uint8_t index[LED_HITS_TO_REMEMBER];
+  uint16_t tick[LED_HITS_TO_REMEMBER];
+} last_hit_t;
+#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+
+typedef enum rgb_task_states {
+  STARTING,
+  RENDERING,
+  FLUSHING,
+  SYNCING
+} rgb_task_states;
+
+typedef uint8_t led_flags_t;
+
+typedef struct PACKED {
+  uint8_t iter;
+  led_flags_t flags;
+  bool init;
+} effect_params_t;
+
+typedef struct PACKED {
+  // Global tick at 20 Hz
+  uint32_t tick;
+  // Ticks since this key was last hit.
+  uint32_t any_key_hit;
+} rgb_counters_t;
+
+typedef struct PACKED {
+       uint8_t x;
+       uint8_t y;
+} point_t;
+
+typedef union {
+  uint8_t raw;
+  struct {
+    uint8_t row:4; // 16 max
+    uint8_t col:4; // 16 max
+  };
+} matrix_co_t;
+
+typedef struct PACKED {
+       matrix_co_t matrix_co;
+       point_t point;
+       uint8_t modifier:1;
+} rgb_led;
+
+typedef union {
+  uint32_t raw;
+  struct PACKED {
+    bool     enable  :1;
+    uint8_t  mode    :7;
+    uint8_t  hue     :8;
+    uint8_t  sat     :8;
+    uint8_t  val     :8;
+    uint8_t  speed   :8;//EECONFIG needs to be increased to support this
+  };
+} rgb_config_t;
+
+#if defined(_MSC_VER)
+#pragma pack( pop )
+#endif