Refactor animation API to allow for processing entire frames at once

This commit is contained in:
Kuba Birecki 2021-12-19 15:28:21 +01:00
parent ae518fabc9
commit 2fa6063c9b
14 changed files with 179 additions and 207 deletions

13
app/dts/animation.dtsi Normal file
View file

@ -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>;
};
};

View file

@ -1,6 +1,23 @@
# Copyright (c) 2020, The ZMK Contributors # Copyright (c) 2020, The ZMK Contributors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
pixel-cells: properties:
- position_x pixels:
- position_y 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.

View file

@ -7,21 +7,9 @@ description: |
compatible: "zmk,animation-compose" compatible: "zmk,animation-compose"
include: animation_base.yaml
properties: properties:
label:
type: string
required: true
animations: animations:
type: phandles type: phandles
required: true required: true
description: | description: |
Animations to be combined. Animations to be combined.
blending-modes:
type: array
required: true
description: |
Blending modes for each animation.

View file

@ -9,10 +9,6 @@ compatible: "zmk,animation-ripple"
include: animation_base.yaml include: animation_base.yaml
properties: properties:
label:
type: string
required: true
duration: duration:
type: int type: int
required: false required: false

View file

@ -9,10 +9,6 @@ compatible: "zmk,animation-solid"
include: animation_base.yaml include: animation_base.yaml
properties: properties:
label:
type: string
required: true
duration: duration:
type: int type: int
required: false required: false

View file

@ -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

View file

@ -13,12 +13,6 @@ properties:
This array should contain all driver devices responsible for illuminating animated LEDs. 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. 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: pixels:
type: phandle-array type: phandle-array
required: true required: true

View file

@ -21,11 +21,10 @@
*/ */
struct animation_pixel { struct animation_pixel {
const size_t id;
const uint8_t position_x; const uint8_t position_x;
const uint8_t position_y; const uint8_t position_y;
const struct device *animation; struct zmk_color_rgb value;
}; };
#ifdef __cplusplus #ifdef __cplusplus
@ -33,63 +32,23 @@ extern "C" {
#endif #endif
/** /**
* @typedef animation_api_prep_next_frame * @typedef animation_render_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
* @brief Callback API for generating the next animation 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, typedef void (*animation_api_render_frame)(const struct device *dev, struct animation_pixel *pixels,
const struct animation_pixel *pixel, size_t num_pixels);
struct zmk_color_rgb *value);
struct animation_api { struct animation_api {
animation_api_on_before_frame on_before_frame; animation_api_render_frame render_frame;
animation_api_on_after_frame on_after_frame;
animation_api_render_pixel render_pixel;
}; };
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; const struct animation_api *api = (const struct animation_api *)dev->api;
if (api->on_before_frame == NULL) { return api->render_frame(dev, pixels, num_pixels);
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);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -9,6 +9,13 @@
#include <device.h> #include <device.h>
#include <drivers/led_strip.h> #include <drivers/led_strip.h>
#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 { struct zmk_color_rgb {
float r; float r;
float g; float g;
@ -30,7 +37,7 @@ static inline size_t zmk_animation_get_pixel_by_key_position(size_t key_position
#endif #endif
#if defined(CONFIG_ZMK_ANIMATION_PIXEL_DISTANCE) && (CONFIG_ZMK_ANIMATION_PIXEL_DISTANCE == 1) #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 #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, 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_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);
}

View file

@ -35,10 +35,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define PHANDLE_TO_PIXEL(idx, node_id, prop) \ #define PHANDLE_TO_PIXEL(idx, node_id, prop) \
{ \ { \
.id = idx, \
.position_x = DT_PHA_BY_IDX(node_id, prop, idx, position_x), \ .position_x = DT_PHA_BY_IDX(node_id, prop, idx, position_x), \
.position_y = DT_PHA_BY_IDX(node_id, prop, idx, position_y), \ .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)}; 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[] = { static const struct device *animation_root = DEVICE_DT_GET(DT_CHOSEN(zmk_animation));
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);
/** /**
* Pixel configuration. * Pixel configuration.
*/ */
static const struct animation_pixel pixels[] = { static struct animation_pixel pixels[] = {
ZMK_DT_INST_FOREACH_PROP_ELEM(0, pixels, PHANDLE_TO_PIXEL)}; 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. * 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]; return pixel_distance[pixel_idx][other_pixel_idx];
} }
#endif #endif
static void zmk_animation_tick(struct k_work *work) { static void zmk_animation_tick(struct k_work *work) {
for (size_t i = 0; i < animations_size; ++i) { animation_render_frame(animation_root, &pixels[0], pixels_size);
animation_on_before_frame(animations[i]);
}
for (size_t i = 0; i < pixels_size; ++i) { for (size_t i = 0; i < pixels_size; ++i) {
struct zmk_color_rgb rgb = { zmk_rgb_to_led_rgb(&pixels[i].value, &px_buffer[i]);
.r = 0,
.g = 0,
.b = 0,
};
animation_render_pixel(pixels[i].animation, &pixels[i], &rgb);
zmk_rgb_to_led_rgb(&rgb, &px_buffer[i]);
} }
size_t pixels_updated = 0; 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; 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); 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 // Prefill the pixel distance lookup table
for (size_t i = 0; i < pixels_size; ++i) { for (size_t i = 0; i < pixels_size; ++i) {
for (size_t j = 0; j < pixels_size; ++j) { 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) + 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 #endif

View file

@ -12,7 +12,6 @@
#include <logging/log.h> #include <logging/log.h>
#include <zmk/animation.h> #include <zmk/animation.h>
#include <dt-bindings/zmk/animation_compose.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -26,77 +25,31 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct animation_compose_config { struct animation_compose_config {
const struct device **animations; const struct device **animations;
const size_t animations_size; const size_t animations_size;
const uint8_t *blending_modes;
}; };
static void animation_compose_render_pixel(const struct device *dev, static void animation_compose_render_frame(const struct device *dev, struct animation_pixel *pixels,
const struct animation_pixel *pixel, size_t num_pixels) {
struct zmk_color_rgb *value) {
const struct animation_compose_config *config = dev->config; 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) { for (size_t i = 0; i < config->animations_size; ++i) {
animation_render_pixel(animations[i], pixel, animation_render_frame(config->animations[i], pixels, num_pixels);
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;
}
} }
} }
static int animation_compose_init(const struct device *dev) { return 0; } static int animation_compose_init(const struct device *dev) { return 0; }
static const struct animation_api animation_compose_api = { static const struct animation_api animation_compose_api = {
.on_before_frame = NULL, .render_frame = animation_compose_render_frame,
.on_after_frame = NULL,
.render_pixel = animation_compose_render_pixel,
}; };
#define ANIMATION_COMPOSE_DEVICE(idx) \ #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[] = { \ static const struct device *animation_compose_##idx##_animations[] = { \
ZMK_DT_INST_FOREACH_PROP_ELEM(idx, animations, PHANDLE_TO_DEVICE)}; \ ZMK_DT_INST_FOREACH_PROP_ELEM(idx, animations, PHANDLE_TO_DEVICE)}; \
\ \
static struct animation_compose_config animation_compose_##idx##_config = { \ static struct animation_compose_config animation_compose_##idx##_config = { \
.animations = animation_compose_##idx##_animations, \ .animations = animation_compose_##idx##_animations, \
.animations_size = DT_INST_PROP_LEN(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, \ DEVICE_DT_INST_DEFINE(idx, &animation_compose_init, NULL, NULL, \

View file

@ -30,7 +30,10 @@ struct animation_ripple_event {
struct animation_ripple_config { struct animation_ripple_config {
struct zmk_color_hsl *color_hsl; struct zmk_color_hsl *color_hsl;
size_t *pixel_map;
size_t pixel_map_size;
size_t event_buffer_size; size_t event_buffer_size;
uint8_t blending_mode;
uint8_t distance_per_frame; uint8_t distance_per_frame;
uint8_t ripple_width; uint8_t ripple_width;
uint8_t event_frames; uint8_t event_frames;
@ -76,15 +79,38 @@ static int animation_ripple_on_key_press(const struct device *dev, const zmk_eve
return 0; 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; const struct animation_ripple_config *config = dev->config;
struct animation_ripple_data *data = dev->data; struct animation_ripple_data *data = dev->data;
size_t *pixel_map = config->pixel_map;
size_t i = data->events_start; size_t i = data->events_start;
while (i != data->events_end) { while (i != data->events_end) {
struct animation_ripple_event *event = &data->event_buffer[i]; 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) { if (event->counter < config->event_frames) {
event->distance += config->distance_per_frame; event->distance += config->distance_per_frame;
event->counter += 1; 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) { static int animation_ripple_init(const struct device *dev) {
const struct animation_ripple_config *config = dev->config; const struct animation_ripple_config *config = dev->config;
struct animation_ripple_data *data = dev->data; 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 = { static const struct animation_api animation_ripple_api = {
.on_before_frame = NULL, .render_frame = animation_ripple_render_frame,
.on_after_frame = animation_ripple_on_after_frame,
.render_pixel = animation_ripple_render_pixel,
}; };
#define ANIMATION_RIPPLE_DEVICE(idx) \ #define ANIMATION_RIPPLE_DEVICE(idx) \
@ -154,16 +150,21 @@ static const struct animation_api animation_ripple_api = {
.num_events = 0, \ .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 uint32_t animation_ripple_##idx##_color = DT_INST_PROP(idx, color); \
\ \
static struct animation_ripple_config animation_ripple_##idx##_config = { \ static struct animation_ripple_config animation_ripple_##idx##_config = { \
.color_hsl = (struct zmk_color_hsl *)&animation_ripple_##idx##_color, \ .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), \ .event_buffer_size = DT_INST_PROP(idx, buffer_size), \
.blending_mode = DT_INST_PROP(idx, blending_mode), \
.distance_per_frame = \ .distance_per_frame = \
(255 * 1000 / DT_INST_PROP(idx, duration)) / CONFIG_ZMK_ANIMATION_FPS, \ (255 * 1000 / DT_INST_PROP(idx, duration)) / CONFIG_ZMK_ANIMATION_FPS, \
.ripple_width = DT_INST_PROP(idx, ripple_width) / 2, \ .ripple_width = DT_INST_PROP(idx, ripple_width) / 2, \
.event_frames = \ .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, \ DEVICE_DT_INST_DEFINE(idx, &animation_ripple_init, NULL, &animation_ripple_##idx##_data, \

View file

@ -16,6 +16,8 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct animation_solid_config { struct animation_solid_config {
size_t *pixel_map;
size_t pixel_map_size;
struct zmk_color_hsl *colors; struct zmk_color_hsl *colors;
uint8_t num_colors; uint8_t num_colors;
uint16_t duration; uint16_t duration;
@ -31,7 +33,7 @@ struct animation_solid_data {
struct zmk_color_rgb current_rgb; 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; const struct animation_solid_config *config = dev->config;
struct animation_solid_data *data = dev->data; 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; data->counter = (data->counter + 1) % config->duration;
} }
static void animation_solid_render_pixel(const struct device *dev, static void animation_solid_render_frame(const struct device *dev, struct animation_pixel *pixels,
const struct animation_pixel *pixel, size_t num_pixels) {
struct zmk_color_rgb *value) { const struct animation_solid_config *config = dev->config;
const struct animation_solid_data *data = dev->data; struct animation_solid_data *data = dev->data;
value->r = data->current_rgb.r; animation_solid_tick(dev);
value->g = data->current_rgb.g;
value->b = data->current_rgb.b; 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) { 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); 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; return 0;
} }
static const struct animation_api animation_solid_api = { static const struct animation_api animation_solid_api = {
.on_before_frame = animation_solid_on_before_frame, .render_frame = animation_solid_render_frame,
.on_after_frame = NULL,
.render_pixel = animation_solid_render_pixel,
}; };
#define ANIMATION_SOLID_DEVICE(idx) \ #define ANIMATION_SOLID_DEVICE(idx) \
\ \
static struct animation_solid_data animation_solid_##idx##_data; \ static struct animation_solid_data animation_solid_##idx##_data; \
\ \
static uint32_t animation_solid_##idx##_colors[DT_INST_PROP_LEN(idx, colors)] = \ static size_t animation_ripple_##idx##_pixel_map[] = DT_INST_PROP(idx, pixels); \
DT_INST_PROP(idx, colors); \ \
static uint32_t animation_solid_##idx##_colors[] = DT_INST_PROP(idx, colors); \
\ \
static struct animation_solid_config animation_solid_##idx##_config = { \ 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, \ .colors = (struct zmk_color_hsl *)animation_solid_##idx##_colors, \
.num_colors = DT_INST_PROP_LEN(idx, colors), \ .num_colors = DT_INST_PROP_LEN(idx, colors), \
.duration = DT_INST_PROP(idx, duration) * CONFIG_ZMK_ANIMATION_FPS, \ .duration = DT_INST_PROP(idx, duration) * CONFIG_ZMK_ANIMATION_FPS, \

View file

@ -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->s = from->s - (from->s - to->s) * step;
result->l = from->l - (from->l - to->l) * 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;
}