diff --git a/src/blob.rs b/src/blob.rs index cbc3d8a33..8600a7c15 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -6,9 +6,13 @@ use std::fs; use std::io::Write; use std::path::{Path, PathBuf}; +use self::image::GenericImageView; +use crate::constants::AVATAR_SIZE; use crate::context::Context; use crate::events::Event; +extern crate image; + /// Represents a file in the blob directory. /// /// The object has a name, which will always be valid UTF-8. Having a @@ -349,6 +353,31 @@ impl<'a> BlobObject<'a> { } true } + + pub fn recode_to_avatar_size(&self, context: &Context) -> Result<(), BlobError> { + let blob_abs = self.to_abs_path(); + let img = image::open(&blob_abs).map_err(|err| BlobError::RecodeFailure { + blobdir: context.get_blobdir().to_path_buf(), + blobname: blob_abs.to_str().unwrap_or_default().to_string(), + cause: err, + backtrace: failure::Backtrace::new(), + })?; + + if img.width() <= AVATAR_SIZE && img.height() <= AVATAR_SIZE { + return Ok(()); + } + + let img = img.thumbnail(AVATAR_SIZE, AVATAR_SIZE); + + img.save(&blob_abs).map_err(|err| BlobError::WriteFailure { + blobdir: context.get_blobdir().to_path_buf(), + blobname: blob_abs.to_str().unwrap_or_default().to_string(), + cause: err, + backtrace: failure::Backtrace::new(), + })?; + + Ok(()) + } } impl<'a> fmt::Display for BlobObject<'a> { @@ -382,6 +411,13 @@ pub enum BlobError { cause: std::io::Error, backtrace: failure::Backtrace, }, + RecodeFailure { + blobdir: PathBuf, + blobname: String, + #[cause] + cause: image::ImageError, + backtrace: failure::Backtrace, + }, WrongBlobdir { blobdir: PathBuf, src: PathBuf, @@ -429,6 +465,9 @@ impl fmt::Display for BlobError { blobname, blobdir.display(), ), + BlobError::RecodeFailure { + blobdir, blobname, .. + } => write!(f, "Failed to recode {} in {}", blobname, blobdir.display(),), BlobError::WrongBlobdir { blobdir, src, .. } => write!( f, "File path {} is not in blobdir {}", diff --git a/src/chat.rs b/src/chat.rs index 33a742e0d..21fe8c9c5 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1883,6 +1883,7 @@ pub fn set_chat_profile_image( _ => Err(err), }, )?; + image_blob.recode_to_avatar_size(context)?; chat.param.set(Param::ProfileImage, image_blob.as_name()); msg.param.set(Param::Arg, image_blob.as_name()); msg.text = Some(context.stock_system_msg( diff --git a/src/config.rs b/src/config.rs index fc760eef7..bd19512a7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -136,6 +136,7 @@ impl Context { match value { Some(value) => { let blob = BlobObject::new_from_path(&self, value)?; + blob.recode_to_avatar_size(self)?; self.sql.set_raw_config(self, key, Some(blob.as_name())) } None => self.sql.set_raw_config(self, key, None), diff --git a/src/constants.rs b/src/constants.rs index bbd3d8e99..ac4f29c1f 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -184,6 +184,9 @@ pub const DC_VC_CONTACT_CONFIRM: i32 = 6; pub const DC_BOB_ERROR: i32 = 0; pub const DC_BOB_SUCCESS: i32 = 1; +// max. width/height of an avatar +pub const AVATAR_SIZE: u32 = 192; + #[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)] #[repr(i32)] pub enum Viewtype {