/* * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ #include #include static float fmod(float a, float b) { float mod = a < 0 ? -a : a; float x = b < 0 ? -b : b; while (mod >= x) { mod = mod - x; } return a < 0 ? -mod : mod; } static float fabs(float a) { return a < 0 ? -a : a; } /** * HSL chosen over HSV/HSB as it shares the same parameters with LCh or HSLuv. * The latter color spaces could be interesting to experiment with because of their * perceptual uniformity, but it would come at the cost of some performance. * Using the same parameters would make it easy to toggle any such behavior * using a single config flag. * * Algorithm source: https://www.tlbx.app/color-converter */ void zmk_hsl_to_rgb(const struct zmk_color_hsl *hsl, struct zmk_color_rgb *rgb) { float s = (float)hsl->s / 100; float l = (float)hsl->l / 100; float a = (float)hsl->h / 60; float chroma = s * (1 - fabs(2 * l - 1)); float x = chroma * (1 - fabs(fmod(a, 2) - 1)); float m = l - chroma / 2; switch ((uint8_t)a % 6) { case 0: rgb->r = m + chroma; rgb->g = m + x; rgb->b = m; break; case 1: rgb->r = m + x; rgb->g = m + chroma; rgb->b = m; break; case 2: rgb->r = m; rgb->g = m + chroma; rgb->b = m + x; break; case 3: rgb->r = m; rgb->g = m + x; rgb->b = m + chroma; break; case 4: rgb->r = m + x; rgb->g = m; rgb->b = m + chroma; break; case 5: rgb->r = m + chroma; rgb->g = m; rgb->b = m + x; break; } } /** * Converts ZMKs RGB (float) to Zephyr's led_rgb (uint8_t) format. */ void zmk_rgb_to_led_rgb(const struct zmk_color_rgb *rgb, struct led_rgb *led) { led->r = rgb->r * 255; led->g = rgb->g * 255; led->b = rgb->b * 255; } /** * Compares two HSL colors. */ bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b) { return a->h == b->h && a->s == b->s && a->l == b->l; } /** * Interpolate between two colors using the cylindrical model (HSL). */ void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_color_hsl *to, struct zmk_color_hsl *result, float step) { int16_t hue_delta; hue_delta = from->h - to->h; hue_delta = hue_delta + (180 < abs(hue_delta) ? (hue_delta < 0 ? 360 : -360) : 0); result->h = (uint16_t)(360 + from->h - (hue_delta * step)) % 360; 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; }