1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use {
    crate::{
        gpu::{
            write::{Write, WriteMode},
            BlendMode,
        },
        math::{Coord, Extent, Rect},
        DynScreen, Gpu, Input, Render, Screen,
    },
    std::{
        time::{Duration, Instant},
        u8,
    },
};

// TODO: Specialize with FadeIn, FadeOut, CrossFade versions
/// Visually fades between two `Screen` implementations over time.
///
/// ## Examples
///
/// In order to fade from `Foo` to `Bar` you might:
///
/// ```
/// use {screen_13::prelude_all::*, std::time::Duration};
///
/// fn main() {
///     Engine::default().run(Box::new(Foo))
/// }
///
/// struct Foo;
///
/// impl Screen for Foo {
///     ...
///
///     fn update(self: Box<Self>, gpu: &Gpu, input: &Input) -> DynScreen {
///         let b = Box::new(bar);
///         let t = Duration::from_secs(1.0);
///
///         // The Fade type will call render on (Foo) and bar for u, how handy! 🤖
///         Fade::new(self, b, t)
///     }
/// }
///
/// struct Bar;
///
/// impl Screen for Bar {
///     ...
/// }
///
/// ```
///
/// _Note:_ Screens are only drawn, and not updated, during fade.
pub struct Fade {
    a: Option<DynScreen>,
    b: Option<DynScreen>,
    duration: Duration,
    mode: BlendMode,
    started: Instant,
}

impl Fade {
    /// Constructs a `Fade` from the given `a` and `b` screens and duration.
    pub fn new(a: DynScreen, b: DynScreen, duration: Duration) -> Self {
        Self {
            a: Some(a),
            b: Some(b),
            duration,
            mode: Default::default(),
            started: Instant::now(),
        }
    }

    /// Sets the blend mode for this fade.
    pub fn with_blend_mode(&mut self, mode: BlendMode) {
        self.mode = mode;
    }
}

impl Screen for Fade {
    fn render(&self, gpu: &Gpu, dims: Extent) -> Render {
        // Render each of the a and b screens normally
        let mut a = self.a.as_ref().unwrap().render(gpu, dims);
        let b = self.b.as_ref().unwrap().render(gpu, dims);

        // Figure out `ab` which is 0..1 as we fade from a to b
        let elapsed = match Instant::now() - self.started {
            elapsed if elapsed < self.duration => elapsed,
            _ => self.duration,
        };
        let ab = ((elapsed.as_millis() as f32 / self.duration.as_millis() as f32).min(1.0)
            * u8::MAX as f32) as u8;

        #[cfg(debug_assertions)]
        debug!("Fade AB: {}", ab);

        let dims = b.dims();
        let b = gpu.resolve(b);

        a.write(
            #[cfg(feature = "debug-names")]
            "Fade write B",
        )
        .with_mode(WriteMode::Blend((ab, self.mode)))
        .record(&mut [Write::region(
            &b,
            Rect {
                pos: Coord::ZERO,
                dims,
            },
        )]);

        a
    }

    fn update(mut self: Box<Self>, _: &Gpu, _: &Input) -> DynScreen {
        if Instant::now() - self.started > self.duration {
            self.b.take().unwrap()
        } else {
            self
        }
    }
}