WIP impl album art display
This commit is contained in:
parent
20fd7d4f09
commit
9040f8f88e
3 changed files with 116 additions and 7 deletions
102
src/display.rs
102
src/display.rs
|
@ -2,9 +2,14 @@ use crate::debug_log::log;
|
||||||
use crate::mpd_handler::{InfoFromShared, MPDHandler};
|
use crate::mpd_handler::{InfoFromShared, MPDHandler};
|
||||||
use crate::Opt;
|
use crate::Opt;
|
||||||
use ggez::event::{self, EventHandler};
|
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::Context;
|
||||||
use ggez::GameError;
|
use ggez::GameError;
|
||||||
|
use image::io::Reader as ImageReader;
|
||||||
|
use image::GenericImageView;
|
||||||
|
use std::io::Cursor;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -22,6 +27,11 @@ pub struct MPDDisplay {
|
||||||
shared: Option<InfoFromShared>,
|
shared: Option<InfoFromShared>,
|
||||||
password_entered: bool,
|
password_entered: bool,
|
||||||
dirty_flag: Option<Arc<AtomicBool>>,
|
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 {
|
impl MPDDisplay {
|
||||||
|
@ -36,6 +46,11 @@ impl MPDDisplay {
|
||||||
shared: None,
|
shared: None,
|
||||||
password_entered: false,
|
password_entered: false,
|
||||||
dirty_flag: None,
|
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");
|
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 {
|
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 {
|
if !self.is_valid {
|
||||||
return Err(GameError::EventLoopError(
|
return Err(GameError::EventLoopError(
|
||||||
"Failed to initialize MPDHandler".into(),
|
"Failed to initialize MPDHandler".into(),
|
||||||
|
@ -100,8 +165,26 @@ impl EventHandler for MPDDisplay {
|
||||||
if let Some(shared) = &self.shared {
|
if let Some(shared) = &self.shared {
|
||||||
if self.notice_text.contents() != shared.error_text {
|
if self.notice_text.contents() != shared.error_text {
|
||||||
self.notice_text = Text::new(TextFragment::new(shared.error_text.clone()));
|
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> {
|
fn draw(&mut self, ctx: &mut ggez::Context) -> Result<(), GameError> {
|
||||||
graphics::clear(ctx, Color::BLACK);
|
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())?;
|
self.notice_text.draw(ctx, DrawParam::default())?;
|
||||||
|
|
||||||
graphics::present(ctx)
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ fn main() -> Result<(), String> {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("Failed to handle resizing window");
|
.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) => {
|
event::winit_event::WindowEvent::ReceivedCharacter(ch) => {
|
||||||
display.text_input_event(ctx, ch);
|
display.text_input_event(ctx, ch);
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub struct InfoFromShared {
|
||||||
pub struct MPDHandler {
|
pub struct MPDHandler {
|
||||||
art_data: Vec<u8>,
|
art_data: Vec<u8>,
|
||||||
art_data_size: usize,
|
art_data_size: usize,
|
||||||
|
art_data_type: String,
|
||||||
current_song_filename: String,
|
current_song_filename: String,
|
||||||
current_song_title: String,
|
current_song_title: String,
|
||||||
current_song_artist: String,
|
current_song_artist: String,
|
||||||
|
@ -251,6 +252,7 @@ impl MPDHandler {
|
||||||
let s = Arc::new(RwLock::new(Self {
|
let s = Arc::new(RwLock::new(Self {
|
||||||
art_data: Vec::new(),
|
art_data: Vec::new(),
|
||||||
art_data_size: 0,
|
art_data_size: 0,
|
||||||
|
art_data_type: String::new(),
|
||||||
current_song_filename: String::new(),
|
current_song_filename: String::new(),
|
||||||
current_song_title: String::new(),
|
current_song_title: String::new(),
|
||||||
current_song_artist: String::new(),
|
current_song_artist: String::new(),
|
||||||
|
@ -294,10 +296,10 @@ impl MPDHandler {
|
||||||
Ok(s)
|
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 let Ok(read_lock) = h.try_read() {
|
||||||
if read_lock.art_data.len() == read_lock.art_data_size {
|
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 {
|
'handle_buf: loop {
|
||||||
if write_handle.current_binary_size > 0 {
|
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;
|
let count = write_handle.current_binary_size;
|
||||||
write_handle.art_data.extend_from_slice(&buf_vec[0..count]);
|
write_handle.art_data.extend_from_slice(&buf_vec[0..count]);
|
||||||
buf_vec = buf_vec.split_off(count + 1);
|
buf_vec = buf_vec.split_off(count + 1);
|
||||||
write_handle.current_binary_size = 0;
|
write_handle.current_binary_size = 0;
|
||||||
write_handle.poll_state = PollState::None;
|
write_handle.poll_state = PollState::None;
|
||||||
write_handle.dirty_flag.store(true, Ordering::Relaxed);
|
log(format!(
|
||||||
log("Got complete album art chunk");
|
"Album art recv progress: {}/{}",
|
||||||
|
write_handle.art_data.len(),
|
||||||
|
write_handle.art_data_size
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
write_handle.art_data.extend_from_slice(&buf_vec);
|
write_handle.art_data.extend_from_slice(&buf_vec);
|
||||||
write_handle.current_binary_size -= buf_vec.len();
|
write_handle.current_binary_size -= buf_vec.len();
|
||||||
|
@ -455,6 +460,9 @@ impl MPDHandler {
|
||||||
write_handle.art_data.len(),
|
write_handle.art_data.len(),
|
||||||
write_handle.art_data_size
|
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;
|
break 'handle_buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -583,6 +591,8 @@ impl MPDHandler {
|
||||||
write_handle.current_song_title = line.split_off(7);
|
write_handle.current_song_title = line.split_off(7);
|
||||||
} else if line.starts_with("Artist: ") {
|
} else if line.starts_with("Artist: ") {
|
||||||
write_handle.current_song_artist = line.split_off(8);
|
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 {
|
} else {
|
||||||
log(format!("WARNING: Got unrecognized/ignored line: {}", line));
|
log(format!("WARNING: Got unrecognized/ignored line: {}", line));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue