Browse Source

utils: Allow to provide the requested size when loading an image

Useful for SVGs, so the renderer can render it at the size we want,
instead of returning the texture at the default size which we resize
later and that might result in a blurry image, defeating the purpose
of using a vector image.
fractal-9
Kévin Commaille 2 years ago
parent
commit
acc625cd20
No known key found for this signature in database
GPG Key ID: C971D9DBC9D678D
  1. 21
      src/components/avatar/editable.rs
  2. 2
      src/components/avatar/editable.ui
  3. 12
      src/components/avatar/image.rs
  4. 2
      src/components/media/content_viewer.rs
  5. 11
      src/session/view/content/room_details/history_viewer/visual_media_item.rs
  6. 2
      src/session/view/content/room_history/message_row/visual_media.rs
  7. 35
      src/utils/media/image.rs

21
src/components/avatar/editable.rs

@ -14,7 +14,10 @@ use super::{AvatarData, AvatarImage};
use crate::{
components::{ActionButton, ActionState},
toast,
utils::{expression, media::image::load_image},
utils::{
expression,
media::image::{load_image, ImageDimensions},
},
};
/// The state of the editable avatar.
@ -73,6 +76,8 @@ mod imp {
#[template_child]
pub stack: TemplateChild<gtk::Stack>,
#[template_child]
pub temp_avatar: TemplateChild<adw::Avatar>,
#[template_child]
pub button_remove: TemplateChild<ActionButton>,
#[template_child]
pub button_edit: TemplateChild<ActionButton>,
@ -332,8 +337,20 @@ impl EditableAvatar {
self.imp().remove_sensitive.set(sensitive);
}
/// The dimensions of the avatar in this widget.
fn avatar_dimensions(&self) -> ImageDimensions {
let scale_factor = self.scale_factor();
let avatar_size = self.imp().temp_avatar.size();
let size = (avatar_size * scale_factor) as u32;
ImageDimensions {
width: size,
height: size,
}
}
async fn set_temp_image_from_file(&self, file: gio::File) {
let paintable = load_image(file).await.ok();
let paintable = load_image(file, Some(self.avatar_dimensions())).await.ok();
self.set_temp_image(paintable);
}

2
src/components/avatar/editable.ui

@ -23,7 +23,7 @@
<object class="GtkStackPage">
<property name="name">temp</property>
<property name="child">
<object class="AdwAvatar">
<object class="AdwAvatar" id="temp_avatar">
<property name="size">128</property>
<property name="show-initials">true</property>
<binding name="text">

12
src/components/avatar/image.rs

@ -199,7 +199,12 @@ impl AvatarImage {
async fn load_inner(&self, uri: OwnedMxcUri) {
let client = self.session().client();
let info = self.info();
let needed_size = self.needed_size();
let dimensions = ImageDimensions {
width: needed_size,
height: needed_size,
};
let downloader = ThumbnailDownloader {
main: ImageSource {
@ -210,10 +215,7 @@ impl AvatarImage {
alt: None,
};
let settings = ThumbnailSettings {
dimensions: ImageDimensions {
width: needed_size,
height: needed_size,
},
dimensions,
method: Method::Crop,
animated: true,
prefer_thumbnail: true,
@ -221,7 +223,7 @@ impl AvatarImage {
match downloader.download_to_file(&client, settings).await {
Ok(file) => {
let paintable = load_image(file).await.ok();
let paintable = load_image(file, Some(dimensions)).await.ok();
self.imp().set_paintable(paintable);
}
Err(error) => error!("Could not fetch avatar: {error}"),

2
src/components/media/content_viewer.rs

@ -197,7 +197,7 @@ impl MediaContentViewer {
.unwrap_or_default();
match content_type {
ContentType::Image => match load_image(file).await {
ContentType::Image => match load_image(file, None).await {
Ok(texture) => {
self.view_image(&texture);
return;

11
src/session/view/content/room_details/history_viewer/visual_media_item.rs

@ -162,12 +162,13 @@ mod imp {
let scale_factor = self.obj().scale_factor();
let size = (THUMBNAIL_SIZE * scale_factor) as u32;
let dimensions = ImageDimensions {
width: size,
height: size,
};
let settings = ThumbnailSettings {
dimensions: ImageDimensions {
width: size,
height: size,
},
dimensions,
method: Method::Scale,
animated: false,
prefer_thumbnail: false,
@ -182,7 +183,7 @@ mod imp {
}
};
match load_image(file).await {
match load_image(file, Some(dimensions)).await {
Ok(paintable) => {
self.picture.set_paintable(Some(&paintable));
}

2
src/session/view/content/room_history/message_row/visual_media.rs

@ -334,7 +334,7 @@ impl MessageVisualMedia {
}
};
match load_image(file).await {
match load_image(file, None).await {
Ok(paintable) => {
let child =
if let Some(child) = imp.media.child().and_downcast::<gtk::Picture>() {

35
src/utils/media/image.rs

@ -73,11 +73,32 @@ async fn image_reader(file: gio::File) -> Result<glycin::Image<'static>, glycin:
}
/// Load the given file as an image into a `GdkPaintable`.
pub async fn load_image(file: gio::File) -> Result<gdk::Paintable, glycin::ErrorCtx> {
///
/// Set `request_dimensions` if the image will be shown at specific dimensions.
/// To show the image at its natural size, set it to `None`.
pub async fn load_image(
file: gio::File,
request_dimensions: Option<ImageDimensions>,
) -> Result<gdk::Paintable, glycin::ErrorCtx> {
let image = image_reader(file).await?;
let frame_request = request_dimensions.map(|request| {
let image_info = image.info();
let original_dimensions = ImageDimensions {
width: image_info.width,
height: image_info.height,
};
original_dimensions.to_image_loader_request(request)
});
let (image, first_frame) = spawn_tokio!(async move {
let first_frame = image.next_frame().await?;
let first_frame = if let Some(frame_request) = frame_request {
image.specific_frame(frame_request).await?
} else {
image.next_frame().await?
};
Ok((image, first_frame))
})
.await
@ -374,6 +395,16 @@ impl ImageDimensions {
Some(self.resize(thumbnail_dimensions, ResizeStrategy::Contain))
}
/// Convert these dimensions to a request for the image loader with the
/// requested dimensions.
pub fn to_image_loader_request(
self,
requested_dimensions: ImageDimensions,
) -> glycin::FrameRequest {
let resized_dimensions = self.resize(requested_dimensions, ResizeStrategy::Cover);
glycin::FrameRequest::new().scale(resized_dimensions.width, resized_dimensions.height)
}
}
impl From<ImageDimensions> for BaseImageInfo {

Loading…
Cancel
Save