Refactor animation API to allow for processing entire frames at once
This commit is contained in:
parent
ae518fabc9
commit
2fa6063c9b
14 changed files with 179 additions and 207 deletions
13
app/dts/animation.dtsi
Normal file
13
app/dts/animation.dtsi
Normal 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>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
10
app/dts/bindings/zmk,animation-pixel.yaml
Normal file
10
app/dts/bindings/zmk,animation-pixel.yaml
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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, \
|
||||||
|
|
|
@ -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, \
|
||||||
|
|
|
@ -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, \
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue