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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use std::{fs::File, path::Path, time::Duration};

use crate::{error::AnimeError, AnimeDataBuffer, AnimeDiagonal, AnimeImage, Pixel};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeFrame {
    /// Precomputed data for the frame. This can be transferred directly to the
    /// the `asusd` daemon over dbus or converted to USB packet with `AnimePacketType::from(buffer)`
    data: AnimeDataBuffer,
    delay: Duration,
}

impl AnimeFrame {
    /// Get the inner data buffer of the gif frame
    #[inline]
    pub fn frame(&self) -> &AnimeDataBuffer {
        &self.data
    }

    /// Get the `Duration` of the delay for this frame
    #[inline]
    pub fn delay(&self) -> Duration {
        self.delay
    }
}

/// Defines the time or animation cycle count to use for a gif
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub enum AnimTime {
    /// Time in milliseconds for animation to run
    Time(Duration),
    /// How many full animation loops to run or how many seconds if image is static
    Count(u32),
    /// Run for infinite time
    Infinite,
    /// Fade in, play for, fade out
    Fade(Fade),
}

impl Default for AnimTime {
    #[inline]
    fn default() -> Self {
        Self::Infinite
    }
}

/// Fancy brightness control: fade in/out, show at brightness for n time
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub struct Fade {
    fade_in: Duration,
    show_for: Option<Duration>,
    fade_out: Duration,
}

impl Fade {
    pub fn new(fade_in: Duration, show_for: Option<Duration>, fade_out: Duration) -> Self {
        Self {
            fade_in,
            show_for,
            fade_out,
        }
    }

    pub fn fade_in(&self) -> Duration {
        self.fade_in
    }

    pub fn show_for(&self) -> Option<Duration> {
        self.show_for
    }

    pub fn fade_out(&self) -> Duration {
        self.fade_out
    }

    pub fn total_fade_time(&self) -> Duration {
        self.fade_in + self.fade_out
    }
}

/// A gif animation. This is a collection of frames from the gif, and a duration
/// that the animation should be shown for.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeGif(Vec<AnimeFrame>, AnimTime);

impl AnimeGif {
    /// Create an animation using the 74x36 ASUS gif format
    #[inline]
    pub fn from_diagonal_gif(
        file_name: &Path,
        duration: AnimTime,
        brightness: f32,
    ) -> Result<Self, AnimeError> {
        let mut matrix = AnimeDiagonal::new(None);

        let mut decoder = gif::DecodeOptions::new();
        // Configure the decoder such that it will expand the image to RGBA.
        decoder.set_color_output(gif::ColorOutput::RGBA);
        // Read the file header
        let file = File::open(file_name)?;
        let mut decoder = decoder.read_info(file)?;

        let mut frames = Vec::with_capacity(decoder.buffer_size());

        while let Some(frame) = decoder.read_next_frame()? {
            let wait = frame.delay * 10;
            if matches!(frame.dispose, gif::DisposalMethod::Background) {
                frames = Vec::new();
            }
            for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
                for (x, px) in row.chunks(4).enumerate() {
                    if px[3] != 255 {
                        // should be t but not in some gifs? What, ASUS, what?
                        continue;
                    }
                    matrix.get_mut()[y + frame.top as usize][x + frame.left as usize] =
                        (px[0] as f32 * brightness) as u8;
                }
            }

            frames.push(AnimeFrame {
                data: <AnimeDataBuffer>::from(&matrix),
                delay: Duration::from_millis(wait as u64),
            });
        }
        Ok(Self(frames, duration))
    }

    /// Create an animation using the 74x36 ASUS gif format from a png
    #[inline]
    pub fn from_diagonal_png(
        file_name: &Path,
        duration: AnimTime,
        brightness: f32,
    ) -> Result<Self, AnimeError> {
        let image = AnimeDiagonal::from_png(file_name, None, brightness)?;

        let mut total = Duration::from_millis(1000);
        if let AnimTime::Fade(fade) = duration {
            total = fade.total_fade_time();
            if let Some(middle) = fade.show_for {
                total += middle;
            }
        }
        // Make frame delay 30ms, and find frame count
        let frame_count = total.as_millis() / 30;

        let single = AnimeFrame {
            data: <AnimeDataBuffer>::from(&image),
            delay: Duration::from_millis(30),
        };
        let frames = vec![single; frame_count as usize];

        Ok(Self(frames, duration))
    }

    /// Create an animation using a gif of any size. This method must precompute the
    /// result.
    #[inline]
    pub fn from_gif(
        file_name: &Path,
        scale: f32,
        angle: f32,
        translation: Vec2,
        duration: AnimTime,
        brightness: f32,
    ) -> Result<Self, AnimeError> {
        let mut frames = Vec::new();

        let mut decoder = gif::DecodeOptions::new();
        // Configure the decoder such that it will expand the image to RGBA.
        decoder.set_color_output(gif::ColorOutput::RGBA);
        // Read the file header
        let file = File::open(file_name)?;
        let mut decoder = decoder.read_info(file)?;

        let height = decoder.height();
        let width = decoder.width();
        let pixels: Vec<Pixel> =
            vec![Pixel::default(); (decoder.width() as u32 * decoder.height() as u32) as usize];
        let mut image = AnimeImage::new(
            Vec2::new(scale, scale),
            angle,
            translation,
            brightness,
            pixels,
            decoder.width() as u32,
        );

        while let Some(frame) = decoder.read_next_frame()? {
            let wait = frame.delay * 10;
            if matches!(frame.dispose, gif::DisposalMethod::Background) {
                let pixels: Vec<Pixel> =
                    vec![Pixel::default(); (width as u32 * height as u32) as usize];
                image = AnimeImage::new(
                    Vec2::new(scale, scale),
                    angle,
                    translation,
                    brightness,
                    pixels,
                    width as u32,
                );
            }
            for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() {
                for (x, px) in row.chunks(4).enumerate() {
                    if px[3] != 255 {
                        // should be t but not in some gifs? What, ASUS, what?
                        continue;
                    }
                    let pos =
                        (x + frame.left as usize) + ((y + frame.top as usize) * width as usize);
                    image.get_mut()[pos] = Pixel {
                        color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3),
                        alpha: 1.0,
                    };
                }
            }
            image.update();

            frames.push(AnimeFrame {
                data: <AnimeDataBuffer>::from(&image),
                delay: Duration::from_millis(wait as u64),
            });
        }
        Ok(Self(frames, duration))
    }

    /// Make a static gif out of a greyscale png. If no duration is specified then the default
    /// will be 1 second long. If `AnimTime::Cycles` is specified for `duration` then this can
    /// be considered how many seconds the image will show for.
    #[inline]
    pub fn from_png(
        file_name: &Path,
        scale: f32,
        angle: f32,
        translation: Vec2,
        duration: AnimTime,
        brightness: f32,
    ) -> Result<Self, AnimeError> {
        let image = AnimeImage::from_png(file_name, scale, angle, translation, brightness)?;

        let mut total = Duration::from_millis(1000);
        if let AnimTime::Fade(fade) = duration {
            total = fade.total_fade_time();
            if let Some(middle) = fade.show_for {
                total += middle;
            }
        }
        // Make frame delay 30ms, and find frame count
        let frame_count = total.as_millis() / 30;

        let single = AnimeFrame {
            data: <AnimeDataBuffer>::from(&image),
            delay: Duration::from_millis(30),
        };
        let frames = vec![single; frame_count as usize];

        Ok(Self(frames, duration))
    }

    /// Get a slice of the frames this gif has
    #[inline]
    pub fn frames(&self) -> &[AnimeFrame] {
        &self.0
    }

    /// Get the time/count for this gif
    #[inline]
    pub fn duration(&self) -> AnimTime {
        self.1
    }

    /// Get the frame count
    pub fn frame_count(&self) -> usize {
        self.0.len()
    }

    /// Get total gif time for one run
    pub fn total_frame_time(&self) -> Duration {
        let mut time = 0;
        self.0.iter().for_each(|f| time += f.delay.as_millis());
        Duration::from_millis(time as u64)
    }
}