]> git.seodisparate.com - mpd_info_screen/commitdiff
WIP impl album art display
authorStephen Seo <seo.disparate@gmail.com>
Wed, 15 Dec 2021 07:34:30 +0000 (16:34 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 15 Dec 2021 07:34:30 +0000 (16:34 +0900)
src/display.rs
src/main.rs
src/mpd_handler.rs

index c55c03642e8617f914c71b9bdc40d8c7be43cb56..f1a20acbd8ab53744dad82a7c36837efef6bf54d 100644 (file)
@@ -2,9 +2,14 @@ use crate::debug_log::log;
 use crate::mpd_handler::{InfoFromShared, MPDHandler};
 use crate::Opt;
 use ggez::event::{self, EventHandler};
-use ggez::graphics::{self, Color, DrawParam, Drawable, Text, TextFragment};
+use ggez::graphics::{
+    self, Color, DrawParam, Drawable, Image, Rect, Text, TextFragment, Transform,
+};
 use ggez::Context;
 use ggez::GameError;
+use image::io::Reader as ImageReader;
+use image::GenericImageView;
+use std::io::Cursor;
 use std::sync::atomic::AtomicBool;
 use std::sync::{Arc, RwLock};
 use std::thread;
@@ -22,6 +27,11 @@ pub struct MPDDisplay {
     shared: Option<InfoFromShared>,
     password_entered: bool,
     dirty_flag: Option<Arc<AtomicBool>>,
+    album_art: Option<Image>,
+    album_art_draw_transform: Option<Transform>,
+    filename_text: Text,
+    artist_text: Text,
+    title_text: Text,
 }
 
 impl MPDDisplay {
@@ -36,6 +46,11 @@ impl MPDDisplay {
             shared: None,
             password_entered: false,
             dirty_flag: None,
+            album_art: None,
+            album_art_draw_transform: None,
+            filename_text: Text::new(""),
+            artist_text: Text::new(""),
+            title_text: Text::new(""),
         }
     }
 
@@ -64,10 +79,60 @@ impl MPDDisplay {
             log("Failed to initialize MPDHandler");
         }
     }
+
+    fn get_album_art_transform(&mut self, ctx: &mut Context, fill: bool) -> () {
+        if fill {
+            unimplemented!("filled image not implemented");
+        } else {
+            if let Some(image) = &self.album_art {
+                let screen_coords: Rect = graphics::screen_coordinates(ctx);
+                let art_rect: Rect = image.dimensions();
+                let offset_x: f32 = (screen_coords.w.abs() - art_rect.w.abs()) / 2.0f32;
+                let offset_y: f32 = (screen_coords.h.abs() - art_rect.h.abs()) / 2.0f32;
+                self.album_art_draw_transform = Some(Transform::Values {
+                    dest: [offset_x, offset_y].into(),
+                    rotation: 0.0f32,
+                    scale: [1.0f32, 1.0f32].into(),
+                    offset: [0.0f32, 0.0f32].into(),
+                });
+            } else {
+                self.album_art_draw_transform = None;
+            }
+        }
+    }
+
+    fn get_image_from_data(
+        &mut self,
+        ctx: &mut Context,
+        data: (Vec<u8>, String),
+    ) -> Result<(), String> {
+        let mut image_format: image::ImageFormat = image::ImageFormat::Png;
+        match data.1.as_str() {
+            "image/png" => image_format = image::ImageFormat::Png,
+            "image/jpg" | "image/jpeg" => image_format = image::ImageFormat::Jpeg,
+            "image/gif" => image_format = image::ImageFormat::Gif,
+            _ => (),
+        }
+        let img = ImageReader::with_format(Cursor::new(data.0), image_format)
+            .decode()
+            .map_err(|e| format!("ERROR: Failed to decode album art image: {}", e))?;
+        let rgba8 = img.to_rgba8();
+        let ggez_img = Image::from_rgba8(
+            ctx,
+            rgba8.width() as u16,
+            rgba8.height() as u16,
+            rgba8.as_raw(),
+        )
+        .map_err(|e| format!("ERROR: Failed to load album art image in ggez Image: {}", e))?;
+
+        self.album_art = Some(ggez_img);
+
+        Ok(())
+    }
 }
 
 impl EventHandler for MPDDisplay {
-    fn update(&mut self, _ctx: &mut ggez::Context) -> Result<(), GameError> {
+    fn update(&mut self, ctx: &mut ggez::Context) -> Result<(), GameError> {
         if !self.is_valid {
             return Err(GameError::EventLoopError(
                 "Failed to initialize MPDHandler".into(),
@@ -100,7 +165,25 @@ impl EventHandler for MPDDisplay {
                 if let Some(shared) = &self.shared {
                     if self.notice_text.contents() != shared.error_text {
                         self.notice_text = Text::new(TextFragment::new(shared.error_text.clone()));
+                        self.title_text = Text::new(TextFragment::new(shared.title.clone()));
+                        self.artist_text = Text::new(TextFragment::new(shared.artist.clone()));
+                        self.filename_text = Text::new(TextFragment::new(shared.filename.clone()));
+                    }
+                }
+                let album_art_data_result =
+                    MPDHandler::get_art_data(self.mpd_handler.clone().unwrap());
+                if let Ok(art_data) = album_art_data_result {
+                    let result = self.get_image_from_data(ctx, art_data);
+                    if let Err(e) = result {
+                        log(e);
+                        self.album_art = None;
+                        self.album_art_draw_transform = None;
+                    } else {
+                        self.get_album_art_transform(ctx, false);
                     }
+                } else {
+                    self.album_art = None;
+                    self.album_art_draw_transform = None;
                 }
             }
         }
@@ -111,6 +194,17 @@ impl EventHandler for MPDDisplay {
     fn draw(&mut self, ctx: &mut ggez::Context) -> Result<(), GameError> {
         graphics::clear(ctx, Color::BLACK);
 
+        if self.album_art.is_some() && self.album_art_draw_transform.is_some() {
+            log("Drawing album_art");
+            self.album_art.as_ref().unwrap().draw(
+                ctx,
+                DrawParam {
+                    trans: self.album_art_draw_transform.unwrap(),
+                    ..Default::default()
+                },
+            )?;
+        }
+
         self.notice_text.draw(ctx, DrawParam::default())?;
 
         graphics::present(ctx)
@@ -157,4 +251,8 @@ impl EventHandler for MPDDisplay {
             }
         }
     }
+
+    fn resize_event(&mut self, ctx: &mut Context, _width: f32, _height: f32) {
+        self.get_album_art_transform(ctx, false);
+    }
 }
index 46c74015c3c302c0e005999640aa6329e4180e9b..70c8b16d2a28c2d01aa167f945e0e6fc54131c04 100644 (file)
@@ -98,6 +98,7 @@ fn main() -> Result<(), String> {
                         },
                     )
                     .expect("Failed to handle resizing window");
+                    display.resize_event(ctx, phys_size.width as f32, phys_size.height as f32);
                 }
                 event::winit_event::WindowEvent::ReceivedCharacter(ch) => {
                     display.text_input_event(ctx, ch);
index 9f22bbdfa27bf27f1fc55309ab6195f0be1a581e..eb35af1d7a4eda7f672dc8f64800454f5102d036 100644 (file)
@@ -34,6 +34,7 @@ pub struct InfoFromShared {
 pub struct MPDHandler {
     art_data: Vec<u8>,
     art_data_size: usize,
+    art_data_type: String,
     current_song_filename: String,
     current_song_title: String,
     current_song_artist: String,
@@ -251,6 +252,7 @@ impl MPDHandler {
         let s = Arc::new(RwLock::new(Self {
             art_data: Vec::new(),
             art_data_size: 0,
+            art_data_type: String::new(),
             current_song_filename: String::new(),
             current_song_title: String::new(),
             current_song_artist: String::new(),
@@ -294,10 +296,10 @@ impl MPDHandler {
         Ok(s)
     }
 
-    pub fn get_art_data(h: Arc<RwLock<Self>>) -> Result<Vec<u8>, ()> {
+    pub fn get_art_data(h: Arc<RwLock<Self>>) -> Result<(Vec<u8>, String), ()> {
         if let Ok(read_lock) = h.try_read() {
             if read_lock.art_data.len() == read_lock.art_data_size {
-                return Ok(read_lock.art_data.clone());
+                return Ok((read_lock.art_data.clone(), read_lock.art_data_type.clone()));
             }
         }
 
@@ -439,14 +441,17 @@ impl MPDHandler {
 
         'handle_buf: loop {
             if write_handle.current_binary_size > 0 {
-                if write_handle.current_binary_size < buf_vec.len() {
+                if write_handle.current_binary_size <= buf_vec.len() {
                     let count = write_handle.current_binary_size;
                     write_handle.art_data.extend_from_slice(&buf_vec[0..count]);
                     buf_vec = buf_vec.split_off(count + 1);
                     write_handle.current_binary_size = 0;
                     write_handle.poll_state = PollState::None;
-                    write_handle.dirty_flag.store(true, Ordering::Relaxed);
-                    log("Got complete album art chunk");
+                    log(format!(
+                        "Album art recv progress: {}/{}",
+                        write_handle.art_data.len(),
+                        write_handle.art_data_size
+                    ));
                 } else {
                     write_handle.art_data.extend_from_slice(&buf_vec);
                     write_handle.current_binary_size -= buf_vec.len();
@@ -455,6 +460,9 @@ impl MPDHandler {
                         write_handle.art_data.len(),
                         write_handle.art_data_size
                     ));
+                    if write_handle.art_data.len() == write_handle.art_data_size {
+                        write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                    }
                     break 'handle_buf;
                 }
             }
@@ -583,6 +591,8 @@ impl MPDHandler {
                     write_handle.current_song_title = line.split_off(7);
                 } else if line.starts_with("Artist: ") {
                     write_handle.current_song_artist = line.split_off(8);
+                } else if line.starts_with("type: ") {
+                    write_handle.art_data_type = line.split_off(6);
                 } else {
                     log(format!("WARNING: Got unrecognized/ignored line: {}", line));
                 }