mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-28 11:11:47 +02:00
Move work on animation implementation
This commit is contained in:
@@ -5,33 +5,143 @@
|
|||||||
* Distributed under terms of the GPL3 license.
|
* Distributed under terms of the GPL3 license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "animation.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
|
#define ANIMATION_INTERNAL_API
|
||||||
|
|
||||||
|
typedef struct easing_curve_parameters {
|
||||||
|
size_t count;
|
||||||
|
double extra0, extra1, extra2, extra3;
|
||||||
|
const double *params, *positions;
|
||||||
|
} easing_curve_parameters;
|
||||||
|
|
||||||
|
typedef double(*easing_curve)(easing_curve_parameters*, double);
|
||||||
|
|
||||||
|
typedef struct animation_function {
|
||||||
|
easing_curve_parameters params;
|
||||||
|
easing_curve curve;
|
||||||
|
double y_at_start, y_size;
|
||||||
|
} animation_function;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct Animation {
|
||||||
|
animation_function *functions;
|
||||||
|
size_t count, capacity;
|
||||||
|
} Animation;
|
||||||
|
|
||||||
|
|
||||||
|
#include "animation.h"
|
||||||
|
|
||||||
|
Animation*
|
||||||
|
alloc_animation(void) {
|
||||||
|
return calloc(1, sizeof(Animation));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
animation_is_valid(const Animation* a) { return a != NULL && a->count > 0; }
|
||||||
|
|
||||||
|
Animation*
|
||||||
|
free_animation(Animation *a) {
|
||||||
|
if (a) {
|
||||||
|
for (size_t i = 0; i < a->count; i++) free((void*)a->functions[i].params.params);
|
||||||
|
free(a->functions);
|
||||||
|
free(a);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static double
|
static double
|
||||||
unit_value(double x) { return MAX(0., MIN(x, 1.)); }
|
unit_value(double x) { return MAX(0., MIN(x, 1.)); }
|
||||||
|
|
||||||
double
|
static double
|
||||||
linear_easing_curve(easing_curve_parameters p, double val) {
|
linear_easing_curve(easing_curve_parameters *p, double val) {
|
||||||
for (size_t i = p.count - 1; i-- > 0;) if (p.positions[i] <= val) return p.params[i];
|
double start_pos = 0, stop_pos = 1, start_val = 0, stop_val = 1;
|
||||||
return p.params[0];
|
for (size_t i = 0; i < p->count; i++) {
|
||||||
|
if (p->positions[i] >= val) {
|
||||||
|
stop_pos = p->positions[i];
|
||||||
|
stop_val = p->params[i];
|
||||||
|
if (i > 0) {
|
||||||
|
start_val = p->params[i-1];
|
||||||
|
start_pos = p->positions[i-1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double frac = (val - start_pos) / (stop_pos - start_pos);
|
||||||
|
return start_val + frac * (stop_val - start_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
static double
|
||||||
cubic_bezier_easing_curve(easing_curve_parameters p, double t) {
|
cubic_bezier_easing_curve(easing_curve_parameters *p, double t) {
|
||||||
const double u = 1. - t, uu = u * u, uuu = uu * u, tt = t * t, ttt = tt * t;
|
const double u = 1. - t, uu = u * u, uuu = uu * u, tt = t * t, ttt = tt * t;
|
||||||
// p0 is start, p3 is end. p1, p2 are control points
|
// p0 is start, p3 is end. p1, p2 are control points
|
||||||
return uuu * p.params[0] + 3 * uu * t * p.params[1] + 3 * u * tt * p.params[2] + ttt * p.params[3];
|
return uuu * p->extra0 + 3 * uu * t * p->extra1 + 3 * u * tt * p->extra2 + ttt * p->extra3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
step_easing_curve(easing_curve_parameters *p, double t) {
|
||||||
|
size_t val_bucket = (size_t)(t * p->count);
|
||||||
|
return p->params[MIN(val_bucket, p->count - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
apply_easing_curve(const Animation *a, double val) {
|
apply_easing_curve(const Animation *a, double val) {
|
||||||
if (a->first_half.curve) {
|
val = unit_value(val);
|
||||||
if (a->second_half.curve) {
|
if (!a->count) return val;
|
||||||
if (val <= 0.5) return unit_value(a->first_half.curve(a->first_half.params, 2 * val));
|
size_t idx = MIN((size_t)(val * a->count), a->count - 1);
|
||||||
return unit_value(a->second_half.curve(a->second_half.params, 2 * (val - 0.5)));
|
animation_function *f = a->functions + idx;
|
||||||
} else return unit_value(a->first_half.curve(a->first_half.params, val));
|
double ans = f->curve(&f->params, val);
|
||||||
} else return (val <= 0.5) ? 1. : 0.;
|
return f->y_at_start + unit_value(ans) * f->y_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static animation_function*
|
||||||
|
init_function(Animation *a, double y_at_start, double y_at_end, easing_curve curve, size_t count) {
|
||||||
|
ensure_space_for(a, functions, animation_function, a->count + 1, capacity, 4, false);
|
||||||
|
animation_function *f = a->functions + a->count++;
|
||||||
|
zero_at_ptr(f);
|
||||||
|
f->y_at_start = y_at_start; f->y_size = y_at_end - y_at_start; f->curve = curve;
|
||||||
|
if (count) {
|
||||||
|
double *p = calloc(count*2, sizeof(double));
|
||||||
|
if (!p) fatal("Out of memory");
|
||||||
|
f->params.params = p;
|
||||||
|
f->params.positions = p + count;
|
||||||
|
f->params.count = 0;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
add_cubic_bezier_animation(Animation *a, double y_at_start, double y_at_end, double start, double p1, double p2, double end) {
|
||||||
|
animation_function *f = init_function(a, y_at_start, y_at_end, cubic_bezier_easing_curve, 4);
|
||||||
|
f->params.extra0 = start; f->params.extra1 = p1; f->params.extra2 = p2; f->params.extra3 = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
add_linear_animation(Animation *a, double y_at_start, double y_at_end, size_t count, const double *params, const double *positions) {
|
||||||
|
animation_function *f = init_function(a, y_at_start, y_at_end, linear_easing_curve, count);
|
||||||
|
const size_t sz = count * sizeof(double);
|
||||||
|
memcpy((void*)f->params.params, params, sz); memcpy((void*)f->params.positions, positions, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
add_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t count, EasingStep step) {
|
||||||
|
animation_function *f = init_function(a, y_at_start, y_at_end, step_easing_curve, count + 2);
|
||||||
|
double jump_size = 1. / count, val = 0.;
|
||||||
|
f->params.count = count;
|
||||||
|
double *params = (double*)f->params.params;
|
||||||
|
switch (step) {
|
||||||
|
case EASING_STEP_START:
|
||||||
|
val = jump_size;
|
||||||
|
f->params.count = count - 1;
|
||||||
|
break;
|
||||||
|
case EASING_STEP_END: break;
|
||||||
|
case EASING_STEP_NONE:
|
||||||
|
f->params.count = count - 1;
|
||||||
|
break;
|
||||||
|
case EASING_STEP_BOTH:
|
||||||
|
jump_size = 1. / (count + 1);
|
||||||
|
val = jump_size;
|
||||||
|
f->params.count = count + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < f->params.count; i++, val += jump_size) params[i] = val;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,21 +8,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef struct easing_curve_parameters {
|
typedef enum { EASING_STEP_START, EASING_STEP_END, EASING_STEP_NONE, EASING_STEP_BOTH } EasingStep;
|
||||||
size_t count;
|
#ifndef ANIMATION_INTERNAL_API
|
||||||
const double *params, *positions;
|
typedef struct {int x;} *Animation;
|
||||||
} easing_curve_parameters;
|
#endif
|
||||||
|
Animation* alloc_animation(void);
|
||||||
typedef double(*easing_curve)(easing_curve_parameters, double);
|
|
||||||
|
|
||||||
typedef struct Animation {
|
|
||||||
struct {
|
|
||||||
easing_curve_parameters params;
|
|
||||||
easing_curve curve;
|
|
||||||
} first_half, second_half;
|
|
||||||
} Animation;
|
|
||||||
|
|
||||||
double linear_easing_curve(easing_curve_parameters, double);
|
|
||||||
double cubic_bezier_easing_curve(easing_curve_parameters, double);
|
|
||||||
double apply_easing_curve(const Animation *a, double t /* must be between 0 and 1*/);
|
double apply_easing_curve(const Animation *a, double t /* must be between 0 and 1*/);
|
||||||
|
bool animation_is_valid(const Animation *a);
|
||||||
|
void add_cubic_bezier_animation(Animation *a, double y_at_start, double y_at_end, double start, double p1, double p2, double end);
|
||||||
|
void add_linear_animation(Animation *a, double y_at_start, double y_at_end, size_t count, const double *params, const double *positions);
|
||||||
|
void add_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t count, EasingStep step);
|
||||||
|
Animation* free_animation(Animation *a);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "fonts.h"
|
#include "fonts.h"
|
||||||
#include "monotonic.h"
|
#include "monotonic.h"
|
||||||
#include "animation.h"
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -676,11 +675,11 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow
|
|||||||
bool cursor_blinking = OPT(cursor_blink_interval) > 0 && !cursor->non_blinking && os_window->is_focused && (OPT(cursor_stop_blinking_after) == 0 || time_since_start_blink <= OPT(cursor_stop_blinking_after));
|
bool cursor_blinking = OPT(cursor_blink_interval) > 0 && !cursor->non_blinking && os_window->is_focused && (OPT(cursor_stop_blinking_after) == 0 || time_since_start_blink <= OPT(cursor_stop_blinking_after));
|
||||||
ans->opacity = 1;
|
ans->opacity = 1;
|
||||||
if (cursor_blinking) {
|
if (cursor_blinking) {
|
||||||
if (OPT(animation.cursor).first_half.curve) {
|
if (animation_is_valid(OPT(animation.cursor))) {
|
||||||
monotonic_t den = OPT(cursor_blink_interval) * 2;
|
monotonic_t den = OPT(cursor_blink_interval) * 2;
|
||||||
monotonic_t time_into_cycle = time_since_start_blink % den;
|
monotonic_t time_into_cycle = time_since_start_blink % den;
|
||||||
double frac_into_cycle = (double)time_into_cycle / (double)den;
|
double frac_into_cycle = (double)time_into_cycle / (double)den;
|
||||||
ans->opacity = (float)apply_easing_curve(&OPT(animation.cursor), frac_into_cycle);
|
ans->opacity = (float)apply_easing_curve(OPT(animation.cursor), frac_into_cycle);
|
||||||
set_maximum_wait(ms_to_monotonic_t(75));
|
set_maximum_wait(ms_to_monotonic_t(75));
|
||||||
} else {
|
} else {
|
||||||
monotonic_t n = time_since_start_blink / OPT(cursor_blink_interval);
|
monotonic_t n = time_since_start_blink / OPT(cursor_blink_interval);
|
||||||
|
|||||||
@@ -31,11 +31,11 @@
|
|||||||
#define LIKELY(x) __builtin_expect (!!(x), 1)
|
#define LIKELY(x) __builtin_expect (!!(x), 1)
|
||||||
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
|
#define UNLIKELY(x) __builtin_expect (!!(x), 0)
|
||||||
#define MAX(x, y) __extension__ ({ \
|
#define MAX(x, y) __extension__ ({ \
|
||||||
__typeof__ (x) a = (x); __typeof__ (y) b = (y); \
|
__typeof__ (x) __a__ = (x); __typeof__ (y) __b__ = (y); \
|
||||||
a > b ? a : b;})
|
__a__ > __b__ ? __a__ : __b__;})
|
||||||
#define MIN(x, y) __extension__ ({ \
|
#define MIN(x, y) __extension__ ({ \
|
||||||
__typeof__ (x) a = (x); __typeof__ (y) b = (y); \
|
__typeof__ (x) __a__ = (x); __typeof__ (y) __b__ = (y); \
|
||||||
a < b ? a : b;})
|
__a__ < __b__ ? __a__ : __b__;})
|
||||||
#define SWAP(x, y) do { __typeof__(x) _sw_ = y; y = x; x = _sw_; } while(0)
|
#define SWAP(x, y) do { __typeof__(x) _sw_ = y; y = x; x = _sw_; } while(0)
|
||||||
#define xstr(s) str(s)
|
#define xstr(s) str(s)
|
||||||
#define str(s) #s
|
#define str(s) #s
|
||||||
|
|||||||
@@ -1462,6 +1462,7 @@ finalize(void) {
|
|||||||
F(background_image); F(bell_path); F(bell_theme); F(default_window_logo);
|
F(background_image); F(bell_path); F(bell_theme); F(default_window_logo);
|
||||||
#undef F
|
#undef F
|
||||||
Py_CLEAR(options_object);
|
Py_CLEAR(options_object);
|
||||||
|
free_animation(OPT(animation.cursor));
|
||||||
// we leak the texture here since it is not guaranteed
|
// we leak the texture here since it is not guaranteed
|
||||||
// that freeing the texture will work during shutdown and
|
// that freeing the texture will work during shutdown and
|
||||||
// the GPU driver should take care of it when the OpenGL context is
|
// the GPU driver should take care of it when the OpenGL context is
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ typedef struct {
|
|||||||
hb_feature_t *features;
|
hb_feature_t *features;
|
||||||
} *entries;
|
} *entries;
|
||||||
} font_features;
|
} font_features;
|
||||||
struct { Animation cursor; } animation;
|
struct { Animation *cursor; } animation;
|
||||||
} Options;
|
} Options;
|
||||||
|
|
||||||
typedef struct WindowLogoRenderData {
|
typedef struct WindowLogoRenderData {
|
||||||
|
|||||||
Reference in New Issue
Block a user