From 2fa6063c9baf6e42da8a67645e7ff52e3cd820ed Mon Sep 17 00:00:00 2001 From: Kuba Birecki Date: Sun, 19 Dec 2021 15:28:21 +0100 Subject: [PATCH] Refactor animation API to allow for processing entire frames at once --- app/dts/animation.dtsi | 13 ++++ .../bindings/animations/animation_base.yaml | 23 ++++++- .../animations/zmk,animation-compose.yaml | 12 ---- .../animations/zmk,animation-ripple.yaml | 4 -- .../animations/zmk,animation-solid.yaml | 4 -- app/dts/bindings/zmk,animation-pixel.yaml | 10 +++ app/dts/bindings/zmk,animation.yaml | 6 -- app/include/drivers/animation.h | 59 +++------------- app/include/zmk/animation.h | 22 +++++- app/src/animation/animation.c | 41 +++--------- app/src/animation/animation_compose.c | 55 ++------------- app/src/animation/animation_ripple.c | 67 ++++++++++--------- app/src/animation/animation_solid.c | 37 ++++++---- app/src/animation/color.c | 33 +++++++++ 14 files changed, 179 insertions(+), 207 deletions(-) create mode 100644 app/dts/animation.dtsi create mode 100644 app/dts/bindings/zmk,animation-pixel.yaml diff --git a/app/dts/animation.dtsi b/app/dts/animation.dtsi new file mode 100644 index 00000000..15ed2145 --- /dev/null +++ b/app/dts/animation.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + /omit-if-no-ref/ pixel: animation_pixel { + compatible = "zmk,animation-pixel"; + label = "PIXEL"; + #pixel-cells = <2>; + }; +}; diff --git a/app/dts/bindings/animations/animation_base.yaml b/app/dts/bindings/animations/animation_base.yaml index aa2a5342..a8f10655 100644 --- a/app/dts/bindings/animations/animation_base.yaml +++ b/app/dts/bindings/animations/animation_base.yaml @@ -1,6 +1,23 @@ # Copyright (c) 2020, The ZMK Contributors # SPDX-License-Identifier: MIT -pixel-cells: - - position_x - - position_y +properties: + pixels: + type: array + required: true + description: | + Pixel positions to which the animation should apply. + + blending-mode: + type: int + required: false + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + default: 0 + description: | + Blending mode for the animation to use during render. diff --git a/app/dts/bindings/animations/zmk,animation-compose.yaml b/app/dts/bindings/animations/zmk,animation-compose.yaml index 97bdb1ad..d91f0a2a 100644 --- a/app/dts/bindings/animations/zmk,animation-compose.yaml +++ b/app/dts/bindings/animations/zmk,animation-compose.yaml @@ -7,21 +7,9 @@ description: | compatible: "zmk,animation-compose" -include: animation_base.yaml - properties: - label: - type: string - required: true - animations: type: phandles required: true description: | Animations to be combined. - - blending-modes: - type: array - required: true - description: | - Blending modes for each animation. diff --git a/app/dts/bindings/animations/zmk,animation-ripple.yaml b/app/dts/bindings/animations/zmk,animation-ripple.yaml index e611a6f5..88f5416c 100644 --- a/app/dts/bindings/animations/zmk,animation-ripple.yaml +++ b/app/dts/bindings/animations/zmk,animation-ripple.yaml @@ -9,10 +9,6 @@ compatible: "zmk,animation-ripple" include: animation_base.yaml properties: - label: - type: string - required: true - duration: type: int required: false diff --git a/app/dts/bindings/animations/zmk,animation-solid.yaml b/app/dts/bindings/animations/zmk,animation-solid.yaml index f175f134..b4247885 100644 --- a/app/dts/bindings/animations/zmk,animation-solid.yaml +++ b/app/dts/bindings/animations/zmk,animation-solid.yaml @@ -9,10 +9,6 @@ compatible: "zmk,animation-solid" include: animation_base.yaml properties: - label: - type: string - required: true - duration: type: int required: false diff --git a/app/dts/bindings/zmk,animation-pixel.yaml b/app/dts/bindings/zmk,animation-pixel.yaml new file mode 100644 index 00000000..2b3b5ebc --- /dev/null +++ b/app/dts/bindings/zmk,animation-pixel.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2020, The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Pixel configuration + +compatible: "zmk,animation-pixel" + +pixel-cells: + - position_x + - position_y diff --git a/app/dts/bindings/zmk,animation.yaml b/app/dts/bindings/zmk,animation.yaml index a2ad35b7..32b90395 100644 --- a/app/dts/bindings/zmk,animation.yaml +++ b/app/dts/bindings/zmk,animation.yaml @@ -13,12 +13,6 @@ properties: This array should contain all driver devices responsible for illuminating animated LEDs. The devices must implement Zephyr's LED Strip Interface and expose a chain-lenght devicetree property. - animations: - type: phandles - required: true - description: | - Handles to all active animations. - pixels: type: phandle-array required: true diff --git a/app/include/drivers/animation.h b/app/include/drivers/animation.h index cba8232b..ad4ad956 100644 --- a/app/include/drivers/animation.h +++ b/app/include/drivers/animation.h @@ -21,11 +21,10 @@ */ struct animation_pixel { - const size_t id; const uint8_t position_x; const uint8_t position_y; - const struct device *animation; + struct zmk_color_rgb value; }; #ifdef __cplusplus @@ -33,63 +32,23 @@ extern "C" { #endif /** - * @typedef animation_api_prep_next_frame - * @brief Callback run before every frame is rendered. - * - * @see animation_api_before_frame() for argument descriptions. - */ -typedef void (*animation_api_on_before_frame)(const struct device *dev); - -/** - * @typedef animation_api_prep_next_frame - * @brief Callback run after every frame is rendered. - * - * @see animation_api_before_frame() for argument descriptions. - */ -typedef void (*animation_api_on_after_frame)(const struct device *dev); - -/** - * @typedef animation_api_prep_next_frame + * @typedef animation_render_frame * @brief Callback API for generating the next animation frame * - * @see animation_prep_next_frame() for argument descriptions. + * @see animation_render_frame() for argument descriptions. */ -typedef void (*animation_api_render_pixel)(const struct device *dev, - const struct animation_pixel *pixel, - struct zmk_color_rgb *value); +typedef void (*animation_api_render_frame)(const struct device *dev, struct animation_pixel *pixels, + size_t num_pixels); struct animation_api { - animation_api_on_before_frame on_before_frame; - animation_api_on_after_frame on_after_frame; - animation_api_render_pixel render_pixel; + animation_api_render_frame render_frame; }; -static inline void animation_on_before_frame(const struct device *dev) { +static inline void animation_render_frame(const struct device *dev, struct animation_pixel *pixels, + size_t num_pixels) { const struct animation_api *api = (const struct animation_api *)dev->api; - if (api->on_before_frame == NULL) { - return; - } - - api->on_before_frame(dev); -} - -static inline void animation_on_after_frame(const struct device *dev) { - const struct animation_api *api = (const struct animation_api *)dev->api; - - if (api->on_after_frame == NULL) { - return; - } - - api->on_after_frame(dev); -} - -static inline void animation_render_pixel(const struct device *dev, - const struct animation_pixel *pixel, - struct zmk_color_rgb *value) { - const struct animation_api *api = (const struct animation_api *)dev->api; - - return api->render_pixel(dev, pixel, value); + return api->render_frame(dev, pixels, num_pixels); } #ifdef __cplusplus diff --git a/app/include/zmk/animation.h b/app/include/zmk/animation.h index aeede8ea..e48b38e3 100644 --- a/app/include/zmk/animation.h +++ b/app/include/zmk/animation.h @@ -9,6 +9,13 @@ #include #include +#define ZMK_ANIMATION_BLENDING_MODE_NORMAL 0 +#define ZMK_ANIMATION_BLENDING_MODE_MULTIPLY 1 +#define ZMK_ANIMATION_BLENDING_MODE_LIGHTEN 2 +#define ZMK_ANIMATION_BLENDING_MODE_DARKEN 3 +#define ZMK_ANIMATION_BLENDING_MODE_SCREEN 4 +#define ZMK_ANIMATION_BLENDING_MODE_SUBTRACT 5 + struct zmk_color_rgb { float r; float g; @@ -30,7 +37,7 @@ static inline size_t zmk_animation_get_pixel_by_key_position(size_t key_position #endif #if defined(CONFIG_ZMK_ANIMATION_PIXEL_DISTANCE) && (CONFIG_ZMK_ANIMATION_PIXEL_DISTANCE == 1) -uint16_t zmk_animation_get_pixel_distance(size_t pixel_idx, size_t other_pixel_idx); +uint8_t zmk_animation_get_pixel_distance(size_t pixel_idx, size_t other_pixel_idx); #endif /** @@ -70,3 +77,16 @@ bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b); */ void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_color_hsl *to, struct zmk_color_hsl *result, float step); + +struct zmk_color_rgb __zmk_apply_blending_mode(struct zmk_color_rgb base_value, + struct zmk_color_rgb blend_value, uint8_t mode); + +static inline struct zmk_color_rgb zmk_apply_blending_mode(struct zmk_color_rgb base_value, + struct zmk_color_rgb blend_value, + uint8_t mode) { + if (mode == ZMK_ANIMATION_BLENDING_MODE_NORMAL) { + return blend_value; + } + + return __zmk_apply_blending_mode(base_value, blend_value, mode); +} diff --git a/app/src/animation/animation.c b/app/src/animation/animation.c index 7fcece19..ac8a86ad 100644 --- a/app/src/animation/animation.c +++ b/app/src/animation/animation.c @@ -35,10 +35,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #define PHANDLE_TO_PIXEL(idx, node_id, prop) \ { \ - .id = idx, \ .position_x = DT_PHA_BY_IDX(node_id, prop, idx, position_x), \ .position_y = DT_PHA_BY_IDX(node_id, prop, idx, position_y), \ - .animation = DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)), \ }, /** @@ -59,20 +57,14 @@ static const uint8_t pixels_per_driver[] = { ZMK_DT_INST_FOREACH_PROP_ELEM(0, drivers, PHANDLE_TO_CHAIN_LENGTH)}; /** - * Pointers to all active animation devices. + * Pointer to the root animation */ -static const struct device *animations[] = { - ZMK_DT_INST_FOREACH_PROP_ELEM(0, animations, PHANDLE_TO_DEVICE)}; - -/** - * Size of the animation device pointers array. - */ -static const size_t animations_size = DT_INST_PROP_LEN(0, animations); +static const struct device *animation_root = DEVICE_DT_GET(DT_CHOSEN(zmk_animation)); /** * Pixel configuration. */ -static const struct animation_pixel pixels[] = { +static struct animation_pixel pixels[] = { ZMK_DT_INST_FOREACH_PROP_ELEM(0, pixels, PHANDLE_TO_PIXEL)}; /** @@ -102,29 +94,19 @@ size_t zmk_animation_get_pixel_by_key_position(size_t key_position) { /** * Lookup table for distance between any two pixels. */ -static uint16_t pixel_distance[DT_INST_PROP_LEN(0, pixels)][DT_INST_PROP_LEN(0, pixels)]; +static uint8_t pixel_distance[DT_INST_PROP_LEN(0, pixels)][DT_INST_PROP_LEN(0, pixels)]; -uint16_t zmk_animation_get_pixel_distance(size_t pixel_idx, size_t other_pixel_idx) { +uint8_t zmk_animation_get_pixel_distance(size_t pixel_idx, size_t other_pixel_idx) { return pixel_distance[pixel_idx][other_pixel_idx]; } #endif static void zmk_animation_tick(struct k_work *work) { - for (size_t i = 0; i < animations_size; ++i) { - animation_on_before_frame(animations[i]); - } + animation_render_frame(animation_root, &pixels[0], pixels_size); for (size_t i = 0; i < pixels_size; ++i) { - struct zmk_color_rgb rgb = { - .r = 0, - .g = 0, - .b = 0, - }; - - animation_render_pixel(pixels[i].animation, &pixels[i], &rgb); - - zmk_rgb_to_led_rgb(&rgb, &px_buffer[i]); + zmk_rgb_to_led_rgb(&pixels[i].value, &px_buffer[i]); } size_t pixels_updated = 0; @@ -134,10 +116,6 @@ static void zmk_animation_tick(struct k_work *work) { pixels_updated += (size_t)pixels_per_driver; } - - for (size_t i = 0; i < animations_size; ++i) { - animation_on_after_frame(animations[i]); - } } K_WORK_DEFINE(animation_work, zmk_animation_tick); @@ -151,8 +129,11 @@ static int zmk_animation_init(const struct device *dev) { // Prefill the pixel distance lookup table for (size_t i = 0; i < pixels_size; ++i) { for (size_t j = 0; j < pixels_size; ++j) { + // Distances are normalized to fit inside 0-255 range to fit inside uint8_t + // for better space efficiency pixel_distance[i][j] = sqrt(pow(pixels[i].position_x - pixels[j].position_x, 2) + - pow(pixels[i].position_y - pixels[j].position_y, 2)); + pow(pixels[i].position_y - pixels[j].position_y, 2)) * + 255 / 360; } } #endif diff --git a/app/src/animation/animation_compose.c b/app/src/animation/animation_compose.c index 25c3d2b5..5acce91e 100644 --- a/app/src/animation/animation_compose.c +++ b/app/src/animation/animation_compose.c @@ -12,7 +12,6 @@ #include #include -#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -26,77 +25,31 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); struct animation_compose_config { const struct device **animations; const size_t animations_size; - const uint8_t *blending_modes; }; -static void animation_compose_render_pixel(const struct device *dev, - const struct animation_pixel *pixel, - struct zmk_color_rgb *value) { +static void animation_compose_render_frame(const struct device *dev, struct animation_pixel *pixels, + size_t num_pixels) { const struct animation_compose_config *config = dev->config; - const struct device **animations = config->animations; - const uint8_t *blending_modes = config->blending_modes; - - struct zmk_color_rgb rgb = { - .r = 0, - .g = 0, - .b = 0, - }; - for (size_t i = 0; i < config->animations_size; ++i) { - animation_render_pixel(animations[i], pixel, - blending_modes[i] == BLENDING_MODE_NORMAL ? value : &rgb); - - switch (blending_modes[i]) { - case BLENDING_MODE_MULTIPLY: - value->r = value->r * rgb.r; - value->g = value->g * rgb.g; - value->b = value->b * rgb.b; - break; - case BLENDING_MODE_LIGHTEN: - value->r = value->r > rgb.r ? value->r : rgb.r; - value->g = value->g > rgb.g ? value->g : rgb.g; - value->b = value->b > rgb.b ? value->b : rgb.b; - break; - case BLENDING_MODE_DARKEN: - value->r = value->r > rgb.r ? rgb.r : value->r; - value->g = value->g > rgb.g ? rgb.g : value->g; - value->b = value->b > rgb.b ? rgb.b : value->b; - break; - case BLENDING_MODE_SCREEN: - value->r = value->r + (1.0f - value->r) * rgb.r; - value->g = value->g + (1.0f - value->g) * rgb.g; - value->b = value->b + (1.0f - value->b) * rgb.b; - break; - case BLENDING_MODE_SUBTRACT: - value->r = value->r - value->r * rgb.r; - value->g = value->g - value->g * rgb.g; - value->b = value->b - value->b * rgb.b; - break; - } + animation_render_frame(config->animations[i], pixels, num_pixels); } } static int animation_compose_init(const struct device *dev) { return 0; } static const struct animation_api animation_compose_api = { - .on_before_frame = NULL, - .on_after_frame = NULL, - .render_pixel = animation_compose_render_pixel, + .render_frame = animation_compose_render_frame, }; #define ANIMATION_COMPOSE_DEVICE(idx) \ \ - static const uint8_t animation_compose_##idx##_blending_modes[] = \ - DT_INST_PROP(idx, blending_modes); \ - \ static const struct device *animation_compose_##idx##_animations[] = { \ ZMK_DT_INST_FOREACH_PROP_ELEM(idx, animations, PHANDLE_TO_DEVICE)}; \ \ static struct animation_compose_config animation_compose_##idx##_config = { \ .animations = animation_compose_##idx##_animations, \ .animations_size = DT_INST_PROP_LEN(idx, animations), \ - .blending_modes = animation_compose_##idx##_blending_modes, \ }; \ \ DEVICE_DT_INST_DEFINE(idx, &animation_compose_init, NULL, NULL, \ diff --git a/app/src/animation/animation_ripple.c b/app/src/animation/animation_ripple.c index 445efda2..b74b4af2 100644 --- a/app/src/animation/animation_ripple.c +++ b/app/src/animation/animation_ripple.c @@ -30,7 +30,10 @@ struct animation_ripple_event { struct animation_ripple_config { struct zmk_color_hsl *color_hsl; + size_t *pixel_map; + size_t pixel_map_size; size_t event_buffer_size; + uint8_t blending_mode; uint8_t distance_per_frame; uint8_t ripple_width; uint8_t event_frames; @@ -76,15 +79,38 @@ static int animation_ripple_on_key_press(const struct device *dev, const zmk_eve return 0; } -static void animation_ripple_on_after_frame(const struct device *dev) { +static void animation_ripple_render_frame(const struct device *dev, struct animation_pixel *pixels, + size_t num_pixels) { const struct animation_ripple_config *config = dev->config; struct animation_ripple_data *data = dev->data; + size_t *pixel_map = config->pixel_map; + size_t i = data->events_start; while (i != data->events_end) { struct animation_ripple_event *event = &data->event_buffer[i]; + // Render all pixels for each event + for (int j = 0; j < config->pixel_map_size; ++j) { + uint8_t pixel_distance = zmk_animation_get_pixel_distance(event->pixel_id, j); + + if (config->ripple_width > abs(pixel_distance - event->distance)) { + float intensity = + (float)abs(pixel_distance - event->distance) / (float)config->ripple_width; + + struct zmk_color_rgb color = { + .r = intensity * data->color_rgb.r, + .g = intensity * data->color_rgb.g, + .b = intensity * data->color_rgb.b, + }; + + pixels[pixel_map[j]].value = zmk_apply_blending_mode(pixels[pixel_map[j]].value, + color, config->blending_mode); + } + } + + // Update event counter if (event->counter < config->event_frames) { event->distance += config->distance_per_frame; event->counter += 1; @@ -99,34 +125,6 @@ static void animation_ripple_on_after_frame(const struct device *dev) { } } -static void animation_ripple_render_pixel(const struct device *dev, - const struct animation_pixel *pixel, - struct zmk_color_rgb *value) { - const struct animation_ripple_config *config = dev->config; - struct animation_ripple_data *data = dev->data; - - size_t i = data->events_start; - - while (i != data->events_end) { - const struct animation_ripple_event *event = &data->event_buffer[i]; - - uint16_t pixel_distance = zmk_animation_get_pixel_distance(event->pixel_id, pixel->id); - - if (config->ripple_width > abs(pixel_distance - event->distance)) { - float intensity = - (float)abs(pixel_distance - event->distance) / (float)config->ripple_width; - - value->r = intensity * data->color_rgb.r; - value->g = intensity * data->color_rgb.g; - value->b = intensity * data->color_rgb.b; - } - - if (++i == config->event_buffer_size) { - i = 0; - } - } -} - static int animation_ripple_init(const struct device *dev) { const struct animation_ripple_config *config = dev->config; struct animation_ripple_data *data = dev->data; @@ -137,9 +135,7 @@ static int animation_ripple_init(const struct device *dev) { } static const struct animation_api animation_ripple_api = { - .on_before_frame = NULL, - .on_after_frame = animation_ripple_on_after_frame, - .render_pixel = animation_ripple_render_pixel, + .render_frame = animation_ripple_render_frame, }; #define ANIMATION_RIPPLE_DEVICE(idx) \ @@ -154,16 +150,21 @@ static const struct animation_api animation_ripple_api = { .num_events = 0, \ }; \ \ + static size_t animation_ripple_##idx##_pixel_map[] = DT_INST_PROP(idx, pixels); \ + \ static uint32_t animation_ripple_##idx##_color = DT_INST_PROP(idx, color); \ \ static struct animation_ripple_config animation_ripple_##idx##_config = { \ .color_hsl = (struct zmk_color_hsl *)&animation_ripple_##idx##_color, \ + .pixel_map = &animation_ripple_##idx##_pixel_map[0], \ + .pixel_map_size = DT_INST_PROP_LEN(idx, pixels), \ .event_buffer_size = DT_INST_PROP(idx, buffer_size), \ + .blending_mode = DT_INST_PROP(idx, blending_mode), \ .distance_per_frame = \ (255 * 1000 / DT_INST_PROP(idx, duration)) / CONFIG_ZMK_ANIMATION_FPS, \ .ripple_width = DT_INST_PROP(idx, ripple_width) / 2, \ .event_frames = \ - 360 / ((255 * 1000 / DT_INST_PROP(idx, duration)) / CONFIG_ZMK_ANIMATION_FPS), \ + 255 / ((255 * 1000 / DT_INST_PROP(idx, duration)) / CONFIG_ZMK_ANIMATION_FPS), \ }; \ \ DEVICE_DT_INST_DEFINE(idx, &animation_ripple_init, NULL, &animation_ripple_##idx##_data, \ diff --git a/app/src/animation/animation_solid.c b/app/src/animation/animation_solid.c index f49041ab..d9d22aa7 100644 --- a/app/src/animation/animation_solid.c +++ b/app/src/animation/animation_solid.c @@ -16,6 +16,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); struct animation_solid_config { + size_t *pixel_map; + size_t pixel_map_size; struct zmk_color_hsl *colors; uint8_t num_colors; uint16_t duration; @@ -31,7 +33,7 @@ struct animation_solid_data { struct zmk_color_rgb current_rgb; }; -static void animation_solid_on_before_frame(const struct device *dev) { +static void animation_solid_tick(const struct device *dev) { const struct animation_solid_config *config = dev->config; struct animation_solid_data *data = dev->data; @@ -57,14 +59,16 @@ static void animation_solid_on_before_frame(const struct device *dev) { data->counter = (data->counter + 1) % config->duration; } -static void animation_solid_render_pixel(const struct device *dev, - const struct animation_pixel *pixel, - struct zmk_color_rgb *value) { - const struct animation_solid_data *data = dev->data; +static void animation_solid_render_frame(const struct device *dev, struct animation_pixel *pixels, + size_t num_pixels) { + const struct animation_solid_config *config = dev->config; + struct animation_solid_data *data = dev->data; - value->r = data->current_rgb.r; - value->g = data->current_rgb.g; - value->b = data->current_rgb.b; + animation_solid_tick(dev); + + for (size_t i = 0; i < config->pixel_map_size; ++i) { + pixels[config->pixel_map[i]].value = data->current_rgb; + } } static int animation_solid_init(const struct device *dev) { @@ -76,23 +80,30 @@ static int animation_solid_init(const struct device *dev) { zmk_hsl_to_rgb(&data->current_hsl, &data->current_rgb); + // if (config->num_colors == 1) { + // return 0; + // } + + // start timer here, so the only option is inline + return 0; } static const struct animation_api animation_solid_api = { - .on_before_frame = animation_solid_on_before_frame, - .on_after_frame = NULL, - .render_pixel = animation_solid_render_pixel, + .render_frame = animation_solid_render_frame, }; #define ANIMATION_SOLID_DEVICE(idx) \ \ static struct animation_solid_data animation_solid_##idx##_data; \ \ - static uint32_t animation_solid_##idx##_colors[DT_INST_PROP_LEN(idx, colors)] = \ - DT_INST_PROP(idx, colors); \ + static size_t animation_ripple_##idx##_pixel_map[] = DT_INST_PROP(idx, pixels); \ + \ + static uint32_t animation_solid_##idx##_colors[] = DT_INST_PROP(idx, colors); \ \ static struct animation_solid_config animation_solid_##idx##_config = { \ + .pixel_map = &animation_ripple_##idx##_pixel_map[0], \ + .pixel_map_size = DT_INST_PROP_LEN(idx, pixels), \ .colors = (struct zmk_color_hsl *)animation_solid_##idx##_colors, \ .num_colors = DT_INST_PROP_LEN(idx, colors), \ .duration = DT_INST_PROP(idx, duration) * CONFIG_ZMK_ANIMATION_FPS, \ diff --git a/app/src/animation/color.c b/app/src/animation/color.c index 25892dc4..55630ec1 100644 --- a/app/src/animation/color.c +++ b/app/src/animation/color.c @@ -103,3 +103,36 @@ void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_colo result->s = from->s - (from->s - to->s) * step; result->l = from->l - (from->l - to->l) * step; } + +struct zmk_color_rgb __zmk_apply_blending_mode(struct zmk_color_rgb base_value, + struct zmk_color_rgb blend_value, uint8_t mode) { + switch (mode) { + case ZMK_ANIMATION_BLENDING_MODE_MULTIPLY: + base_value.r = base_value.r * blend_value.r; + base_value.g = base_value.g * blend_value.g; + base_value.b = base_value.b * blend_value.b; + break; + case ZMK_ANIMATION_BLENDING_MODE_LIGHTEN: + base_value.r = base_value.r > blend_value.r ? base_value.r : blend_value.r; + base_value.g = base_value.g > blend_value.g ? base_value.g : blend_value.g; + base_value.b = base_value.b > blend_value.b ? base_value.b : blend_value.b; + break; + case ZMK_ANIMATION_BLENDING_MODE_DARKEN: + base_value.r = base_value.r > blend_value.r ? blend_value.r : base_value.r; + base_value.g = base_value.g > blend_value.g ? blend_value.g : base_value.g; + base_value.b = base_value.b > blend_value.b ? blend_value.b : base_value.b; + break; + case ZMK_ANIMATION_BLENDING_MODE_SCREEN: + base_value.r = base_value.r + (1.0f - base_value.r) * blend_value.r; + base_value.g = base_value.g + (1.0f - base_value.g) * blend_value.g; + base_value.b = base_value.b + (1.0f - base_value.b) * blend_value.b; + break; + case ZMK_ANIMATION_BLENDING_MODE_SUBTRACT: + base_value.r = base_value.r - base_value.r * blend_value.r; + base_value.g = base_value.g - base_value.g * blend_value.g; + base_value.b = base_value.b - base_value.b * blend_value.b; + break; + } + + return base_value; +}