use alloc::boxed::Box;
use core::ops::Range;
use ratatui_core::{buffer::Buffer, layout::Rect};
use Interpolation::Linear;
use crate::{
default_shader_impl, math, CellFilter, ColorSpace, Duration, Effect, EffectTimer,
Interpolation, Shader,
};
#[derive(Debug, Clone)]
pub(super) struct FreezeAt {
alpha: f32,
set_raw_alpha: bool,
fx: Effect,
}
impl FreezeAt {
pub fn new(alpha: f32, set_raw_alpha: bool, fx: Effect) -> Self {
let alpha = alpha.clamp(0.0, 1.0);
Self { alpha, fx, set_raw_alpha }
}
}
impl Shader for FreezeAt {
default_shader_impl!(clone);
fn name(&self) -> &'static str {
"freeze_at"
}
fn process(&mut self, _duration: Duration, buf: &mut Buffer, area: Rect) -> Option<Duration> {
if let Some(t) = self.fx.timer_mut() {
if !t.started() {
let interpolation = if self.set_raw_alpha { Linear } else { t.interpolation() };
let d = t.remaining().as_secs_f32() * (1.0 - self.alpha);
*t = EffectTimer::new(t.remaining(), interpolation);
t.process(Duration::from_secs_f32(d));
}
}
self.fx
.process(Duration::from_millis(0), buf, area)
}
fn done(&self) -> bool {
self.fx.timer().is_none()
}
fn area(&self) -> Option<Rect> {
self.fx.area()
}
fn set_area(&mut self, area: Rect) {
self.fx.set_area(area);
}
fn filter(&mut self, filter: CellFilter) {
self.fx.filter(filter);
}
fn timer_mut(&mut self) -> Option<&mut EffectTimer> {
None
}
fn timer(&self) -> Option<EffectTimer> {
self.fx.timer()
}
fn cell_filter(&self) -> Option<&CellFilter> {
self.fx.cell_filter()
}
fn set_color_space(&mut self, color_space: ColorSpace) {
self.fx.set_color_space(color_space);
}
fn color_space(&self) -> ColorSpace {
self.fx.color_space()
}
#[cfg(feature = "dsl")]
fn to_dsl(&self) -> Result<crate::dsl::EffectExpression, crate::dsl::DslError> {
crate::dsl::EffectExpression::parse(&format!(
"fx::freeze_at({}, {}, {})",
self.alpha,
self.set_raw_alpha,
self.fx.to_dsl()?
))
}
fn reset(&mut self) {
self.fx.reset();
}
}
#[derive(Debug, Clone)]
pub(super) struct RemapAlpha {
raw_alpha_range: Range<f32>,
fx: Effect,
timer: EffectTimer, rest: f32,
}
impl RemapAlpha {
pub fn new(raw_alpha_range: Range<f32>, fx: Effect) -> Self {
let timer = fx.timer().unwrap_or_default();
let start = raw_alpha_range.start;
let end = raw_alpha_range.end;
let raw_alpha_range = start.clamp(0.0, 1.0)..end.clamp(0.0, 1.0);
let rest = 0.0;
Self { raw_alpha_range, fx, timer, rest }
}
}
impl Shader for RemapAlpha {
default_shader_impl!(clone);
fn name(&self) -> &'static str {
"remap_alpha"
}
fn execute(&mut self, duration: Duration, area: Rect, buf: &mut Buffer) {
if let Some(t) = self.fx.timer_mut() {
if !t.started() {
let skip_initial = t.duration().as_secs_f32() * self.raw_alpha_range.start;
t.process(Duration::from_secs_f32(skip_initial));
}
}
let range = self.raw_alpha_range.end - self.raw_alpha_range.start;
let scaled_duration_ms = 1_000.0 * (duration.as_secs_f32() * range) + self.rest;
self.fx
.process(Duration::from_millis(scaled_duration_ms as _), buf, area);
self.rest = scaled_duration_ms - math::floor(scaled_duration_ms);
}
fn done(&self) -> bool {
self.timer.done()
}
fn area(&self) -> Option<Rect> {
self.fx.area()
}
fn set_area(&mut self, area: Rect) {
self.fx.set_area(area);
}
fn filter(&mut self, filter: CellFilter) {
self.fx.filter(filter);
}
fn timer_mut(&mut self) -> Option<&mut EffectTimer> {
Some(&mut self.timer)
}
fn timer(&self) -> Option<EffectTimer> {
Some(self.timer)
}
fn cell_filter(&self) -> Option<&CellFilter> {
self.fx.cell_filter()
}
fn set_color_space(&mut self, color_space: ColorSpace) {
self.fx.set_color_space(color_space);
}
fn color_space(&self) -> ColorSpace {
self.fx.color_space()
}
#[cfg(feature = "dsl")]
fn to_dsl(&self) -> Result<crate::dsl::EffectExpression, crate::dsl::DslError> {
crate::dsl::EffectExpression::parse(&format!(
"fx::remap_alpha({}, {}, {})",
self.raw_alpha_range.start,
self.raw_alpha_range.end,
self.fx.to_dsl()?
))
}
fn reset(&mut self) {
self.rest = 0.0;
self.timer.reset();
self.fx.reset();
}
}