transition: add a generic animated transition mechanism

This should replace the current fading animation implementation.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-03-19 14:27:50 +00:00
parent 5df19aed20
commit dcab6fc868
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
3 changed files with 223 additions and 1 deletions

View File

@ -10,7 +10,7 @@ base_deps = [
srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c',
'vblank.c') ]
'vblank.c', 'transition.c') ]
picom_inc = include_directories('.')
cflags = []

139
src/transition.c Normal file
View File

@ -0,0 +1,139 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "compiler.h"
#include "transition.h"
#include "utils.h"
/// Get the current value of an `animatable`.
double animatable_get(const struct animatable *a) {
if (a->step_state) {
return a->step_state->current;
}
if (a->duration) {
assert(a->progress < a->duration);
return a->interpolator(a);
}
return a->target;
}
double animatable_get_progress(const struct animatable *a) {
if (a->duration) {
return (double)a->progress / a->duration;
}
return 1;
}
/// Advance the animation by a given number of steps.
void animatable_step(struct animatable *a, unsigned int steps) {
if (!a->duration || !steps) {
return;
}
assert(a->progress < a->duration);
if (steps > a->duration - a->progress) {
steps = a->duration - a->progress;
}
a->progress += steps;
if (a->step_state) {
a->step(a, steps);
}
if (a->progress == a->duration) {
a->start = a->target;
a->duration = 0;
a->progress = 0;
if (a->step_state) {
a->step_state->current = a->target;
}
}
}
/// Returns whether an `animatable` is currently animating.
bool animatable_is_animating(const struct animatable *a) {
assert(!a->duration || a->progress < a->duration);
return a->duration;
}
/// Cancel the current animation of an `animatable`. This stops the animation and
/// the `animatable` will retain its current value.
///
/// Returns true if the `animatable` was animated before this function is called.
bool animatable_cancel(struct animatable *a) {
if (!a->duration) {
return false;
}
a->start = animatable_get(a);
a->target = a->start;
a->duration = 0;
a->progress = 0;
if (a->step_state) {
a->step_state->current = a->start;
}
return true;
}
/// Cancel the current animation of an `animatable` and set its value to its target.
///
/// Returns true if the `animatable` was animated before this function is called.
bool animatable_early_stop(struct animatable *a) {
if (!a->duration) {
return false;
}
a->start = a->target;
a->duration = 0;
a->progress = 0;
if (a->step_state) {
a->step_state->current = a->target;
}
return true;
}
/// Change the target value of an `animatable`.
/// If the `animatable` is already animating, the animation will be canceled first.
void animatable_set_target(struct animatable *a, double target, unsigned int duration) {
animatable_cancel(a);
if (!duration) {
a->start = target;
a->target = target;
return;
}
a->target = target;
a->duration = duration;
a->progress = 0;
if (a->step_state) {
a->step(a, 0);
}
}
/// Create a new animatable.
struct animatable animatable_new(double value, interpolator_fn interpolator, step_fn step) {
assert(!interpolator || !step);
struct animatable ret = {
.start = value,
.target = value,
.duration = 0,
.progress = 0,
.step_state = NULL,
};
if (interpolator) {
ret.interpolator = interpolator;
} else if (step) {
ret.step = step;
step(&ret, 0);
}
return ret;
}
double linear_interpolator(const struct animatable *a) {
double t = (double)a->progress / a->duration;
return (1 - t) * a->start + t * a->target;
}

83
src/transition.h Normal file
View File

@ -0,0 +1,83 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stdbool.h>
struct animatable;
/// The interpolator function for an animatable. This function should calculate the
/// current value of the `animatable` based on its `start`, `target`, `duration` and
/// `progress`.
typedef double (*interpolator_fn)(const struct animatable *);
/// The step function for an animatable. This function should advance the animation state
/// by one step. This function is called _after_ `progress` is incremented. If `progress`
/// is 0 when the function is called, it means an animation has just started, and this
/// function should initialize the state. If `step_state` is NULL when this function is
/// called, this function should allocate and initialize `step_state`.
/// `steps` is the number of steps to advance. This is always 1 or more, unless `progress`
/// is 0 or `step_state` is NULL, in which case `steps` is 0.
typedef void (*step_fn)(struct animatable *, unsigned int steps);
/// The base type for step_state.
struct step_state_base {
/// The current value of the `animatable`.
/// If the `animatable` is not animated, this equals to `animatable->target`.
double current;
};
/// An animatable value
struct animatable {
/// The starting value.
/// When this `animatable` is not animated, this is the current value.
double start;
/// The target value.
/// If the `animatable` is not animated, this equals to `start`.
double target;
/// The animation duration in number of steps.
/// If the `animatable` is not animated, this is 0.
unsigned int duration;
/// The current progress of the animation. From 0 to `duration - 1`.
/// If the `animatable` is not animated, this is 0.
unsigned int progress;
/// Step function state.
struct step_state_base *step_state;
/// The function for calculating the current value. If
/// `step_state` is not NULL, the `step` function is used;
/// otherwise, the `interpolator` function is used.
union {
/// The interpolator function.
interpolator_fn interpolator;
/// The step function.
step_fn step;
};
};
// =============================== API ===============================
/// Get the current value of an `animatable`.
double animatable_get(const struct animatable *a);
/// Get the animation progress as a percentage of the total duration.
double animatable_get_progress(const struct animatable *a);
/// Advance the animation by a given number of steps.
void animatable_step(struct animatable *a, unsigned int steps);
/// Returns whether an `animatable` is currently animating.
bool animatable_is_animating(const struct animatable *a);
/// Cancel the current animation of an `animatable`. This stops the animation and
/// the `animatable` will retain its current value.
///
/// Returns true if the `animatable` was animated before this function is called.
bool animatable_cancel(struct animatable *a);
/// Cancel the current animation of an `animatable` and set its value to its target.
///
/// Returns true if the `animatable` was animated before this function is called.
bool animatable_early_stop(struct animatable *a);
/// Change the target value of an `animatable`.
/// If the `animatable` is already animating, the animation will be canceled first.
void animatable_set_target(struct animatable *a, double target, unsigned int duration);
/// Create a new animatable.
struct animatable animatable_new(double value, interpolator_fn interpolator, step_fn step);
// ========================== Interpolators ==========================
double linear_interpolator(const struct animatable *a);