|
- static const char *TAG = "gradient";
- #include <esp_log.h>
-
- #include "presets.hpp"
- #include "utils.hpp"
-
-
- struct Gradient {
- Color at(Fixed x) const;
-
- static Gradient* make(int n) {
- Gradient *gradient = (Gradient*)malloc(
- sizeof(Gradient) + n * sizeof(*colors)
- );
- gradient->n_colors = n;
- return gradient;
- }
-
- unsigned int n_colors;
- Color colors[];
- };
-
- class GradientPattern : public Pattern {
- public:
- GradientPattern(
- Gradient *_gradient,
- int _cycle_length=20, int _cycle_time_ms=-1,
- bool _reverse=false, bool _march=false
- ) : cycle_length(_cycle_length),
- cycle_time_ms( _cycle_time_ms < 0 ?
- _cycle_length * 500 : _cycle_time_ms
- ),
- reverse(_reverse), march(_march), gradient(_gradient)
- {}
-
- ~GradientPattern() { free(gradient); }
-
- State* start(int) const { return new State(); }
-
- void step(Color[], int, State*) const;
-
- private:
- struct State : Pattern::State {
- State() : last_us(0), offset(0) {}
- int64_t last_us;
- Fixed offset;
- };
-
- int cycle_length;
- int cycle_time_ms;
- bool reverse;
- bool march;
- Gradient *gradient;
- };
-
-
- static inline uint8_t mix(int l, int r, Fixed x) {
- return ((l << shift) + (x * (r - l))) >> shift;
- }
-
- Color Gradient::at(Fixed x) const {
- int i_left = (x * n_colors) >> shift;
- int i_right = i_left < n_colors - 1 ? i_left + 1 : 0;
- Fixed ratio = (x * n_colors) - (i_left << shift);
- //ESP_LOGI(TAG, "at(%d) = %d between %d and %d", x, ratio, i_left, i_right);
- Color cl = colors[i_left];
- Color cr = colors[i_right];
- Color color = {
- mix(cl.r, cr.r, ratio),
- mix(cl.g, cr.g, ratio),
- mix(cl.b, cr.b, ratio)
- };
- /*ESP_LOGI(TAG, "(%d, %d, %d) %f (%d, %d, %d) -> (%d, %d, %d)",
- cl.r, cl.g, cl.b,
- ratio / (float)factor,
- cr.r, cr.g, cr.b,
- color.r, color.g, color.b
- );*/
- return color;
- }
-
- void GradientPattern::step(Color pixels[], int len, Pattern::State *_state) const {
- GradientPattern::State *state = (GradientPattern::State*)_state;
-
- int64_t now_us = time_us();
- int64_t duration_us = state->last_us == 0 ? 0 : now_us - state->last_us;
- state->last_us = now_us;
- //ESP_LOGI(TAG, "duration %d", duration_us);
-
- /*
- int period_us = 1000000 / 60;
- int n_skipped = (duration_us - period_us / 2) / period_us;
- if (n_skipped) {
- ESP_LOGW(TAG, "Skipped %d frames", n_skipped);
- }
- */
- // Don't make a major animation jump if it's been terribly long
- if (duration_us > 100000) duration_us = 100000;
-
-
- int offset_delta = (duration_us << shift) / (cycle_time_ms * 1000);
- if (reverse) state->offset += offset_delta; else state->offset -= offset_delta;
- Fixed off = march ?
- (((int)state->offset * cycle_length) & ~((1 << shift) - 1)) / cycle_length :
- state->offset;
- //ESP_LOGI(TAG, "cycle time %d, delta %d, offset %d, off %d", cycle_time_ms, offset_delta, offset, off);
- for (int i = 0; i < len; ++i) {
- pixels[i] = gradient->at(off + ((i << shift) / cycle_length));
- }
- }
-
-
- Pattern* json_gradient_pattern(cJSON *pattern, const Presets::ColorMap &colors) {
- if (pattern->type != cJSON_Object) {
- ESP_LOGE(TAG, "Not an object: %s", pattern->type);
- return NULL;
- }
- cJSON *jcolors = cJSON_GetObjectItem(pattern, "colors");
- if (!jcolors) {
- ESP_LOGE(TAG, "No colors in %s", pattern->string);
- return NULL;
- }
- if (jcolors->type != cJSON_Array) {
- ESP_LOGE(TAG, "Not an array: %s", jcolors->string);
- return NULL;
- }
-
- Gradient *gradient = Gradient::make(cJSON_GetArraySize(jcolors));
-
- int i = 0;
- cJSON *jcolor;
- cJSON_ArrayForEach(jcolor, jcolors) {
- Color color = Presets::json_color(jcolor, colors);
- gradient->colors[i++] = color;
- }
-
- return new GradientPattern(gradient);
- }
|