Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Final" image polish #3342

Merged
merged 4 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions crates/egui/src/widgets/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl Widget for Button<'_> {

let image_size = if let Some(image) = &image {
image
.load_and_calculate_size(ui, space_available_for_image)
.load_and_calc_size(ui, space_available_for_image)
.unwrap_or(space_available_for_image)
} else {
Vec2::ZERO
Expand Down Expand Up @@ -276,7 +276,7 @@ impl Widget for Button<'_> {
image_size,
);
cursor_x += image_size.x;
let tlr = image.load(ui);
let tlr = image.load_for_size(ui.ctx(), image_size);
widgets::image::paint_texture_load_result(
ui,
&tlr,
Expand Down Expand Up @@ -605,11 +605,12 @@ impl<'a> Widget for ImageButton<'a> {
Vec2::ZERO
};

let tlr = self.image.load(ui);
let texture_size = tlr.as_ref().ok().and_then(|t| t.size());
let available_size_for_image = ui.available_size() - 2.0 * padding;
let tlr = self.image.load_for_size(ui.ctx(), available_size_for_image);
let original_image_size = tlr.as_ref().ok().and_then(|t| t.size());
let image_size = self
.image
.calculate_size(ui.available_size() - 2.0 * padding, texture_size);
.calc_size(available_size_for_image, original_image_size);

let padded_size = image_size + 2.0 * padding;
let (rect, response) = ui.allocate_exact_size(padded_size, self.sense);
Expand Down
65 changes: 38 additions & 27 deletions crates/egui/src/widgets/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,14 @@ impl<'a, T: Into<ImageSource<'a>>> From<T> for Image<'a> {
impl<'a> Image<'a> {
/// Returns the size the image will occupy in the final UI.
#[inline]
pub fn calculate_size(&self, available_size: Vec2, image_size: Option<Vec2>) -> Vec2 {
let image_size = image_size.unwrap_or(Vec2::splat(24.0)); // Fallback for still-loading textures, or failure to load.
self.size.get(available_size, image_size)
pub fn calc_size(&self, available_size: Vec2, original_image_size: Option<Vec2>) -> Vec2 {
let original_image_size = original_image_size.unwrap_or(Vec2::splat(24.0)); // Fallback for still-loading textures, or failure to load.
self.size.calc_size(available_size, original_image_size)
}

pub fn load_and_calculate_size(&self, ui: &mut Ui, available_size: Vec2) -> Option<Vec2> {
let image_size = self.load(ui).ok()?.size()?;
Some(self.size.get(available_size, image_size))
pub fn load_and_calc_size(&self, ui: &mut Ui, available_size: Vec2) -> Option<Vec2> {
let image_size = self.load_for_size(ui.ctx(), available_size).ok()?.size()?;
Some(self.size.calc_size(available_size, image_size))
}

#[inline]
Expand All @@ -269,21 +269,23 @@ impl<'a> Image<'a> {

/// Load the image from its [`Image::source`], returning the resulting [`SizedTexture`].
///
/// The `available_size` is used as a hint when e.g. rendering an svg.
///
/// # Errors
/// May fail if they underlying [`Context::try_load_texture`] call fails.
pub fn load(&self, ui: &Ui) -> TextureLoadResult {
let size_hint = self.size.hint(ui.available_size());
pub fn load_for_size(&self, ctx: &Context, available_size: Vec2) -> TextureLoadResult {
let size_hint = self.size.hint(available_size);
self.source
.clone()
.load(ui.ctx(), self.texture_options, size_hint)
.load(ctx, self.texture_options, size_hint)
}

/// Paint the image in the given rectangle.
#[inline]
pub fn paint_at(&self, ui: &mut Ui, rect: Rect) {
paint_texture_load_result(
ui,
&self.load(ui),
&self.load_for_size(ui.ctx(), rect.size()),
rect,
self.show_loading_spinner,
&self.image_options,
Expand All @@ -293,9 +295,9 @@ impl<'a> Image<'a> {

impl<'a> Widget for Image<'a> {
fn ui(self, ui: &mut Ui) -> Response {
let tlr = self.load(ui);
let texture_size = tlr.as_ref().ok().and_then(|t| t.size());
let ui_size = self.calculate_size(ui.available_size(), texture_size);
let tlr = self.load_for_size(ui.ctx(), ui.available_size());
let original_image_size = tlr.as_ref().ok().and_then(|t| t.size());
let ui_size = self.calc_size(ui.available_size(), original_image_size);

let (rect, response) = ui.allocate_exact_size(ui_size, self.sense);
paint_texture_load_result(
Expand Down Expand Up @@ -364,37 +366,37 @@ impl ImageFit {
}

impl ImageSize {
/// Size hint for e.g. rasterizing an svg.
pub fn hint(&self, available_size: Vec2) -> SizeHint {
if self.maintain_aspect_ratio {
return SizeHint::Scale(1.0.ord());
};

let fit = match self.fit {
let size = match self.fit {
ImageFit::Original { scale } => return SizeHint::Scale(scale.ord()),
ImageFit::Fraction(fract) => available_size * fract,
ImageFit::Exact(size) => size,
};

let fit = fit.min(self.max_size);
let size = size.min(self.max_size);

// TODO(emilk): take pixels_per_point into account here!

// `inf` on an axis means "any value"
match (fit.x.is_finite(), fit.y.is_finite()) {
(true, true) => SizeHint::Size(fit.x.round() as u32, fit.y.round() as u32),
(true, false) => SizeHint::Width(fit.x.round() as u32),
(false, true) => SizeHint::Height(fit.y.round() as u32),
match (size.x.is_finite(), size.y.is_finite()) {
(true, true) => SizeHint::Size(size.x.round() as u32, size.y.round() as u32),
(true, false) => SizeHint::Width(size.x.round() as u32),
(false, true) => SizeHint::Height(size.y.round() as u32),
(false, false) => SizeHint::Scale(1.0.ord()),
}
}

pub fn get(&self, available_size: Vec2, image_size: Vec2) -> Vec2 {
/// Calculate the final on-screen size in points.
pub fn calc_size(&self, available_size: Vec2, original_image_size: Vec2) -> Vec2 {
let Self {
maintain_aspect_ratio,
max_size,
fit,
} = *self;
match fit {
ImageFit::Original { scale } => {
let image_size = image_size * scale;
let image_size = original_image_size * scale;
if image_size.x <= max_size.x && image_size.y <= max_size.y {
image_size
} else {
Expand All @@ -403,11 +405,11 @@ impl ImageSize {
}
ImageFit::Fraction(fract) => {
let scale_to_size = (available_size * fract).min(max_size);
scale_to_fit(image_size, scale_to_size, maintain_aspect_ratio)
scale_to_fit(original_image_size, scale_to_size, maintain_aspect_ratio)
}
ImageFit::Exact(size) => {
let scale_to_size = size.min(max_size);
scale_to_fit(image_size, scale_to_size, maintain_aspect_ratio)
scale_to_fit(original_image_size, scale_to_size, maintain_aspect_ratio)
}
}
}
Expand Down Expand Up @@ -483,6 +485,15 @@ impl<'a> std::fmt::Debug for ImageSource<'a> {
}

impl<'a> ImageSource<'a> {
/// Size of the texture, if known.
#[inline]
pub fn texture_size(&self) -> Option<Vec2> {
match self {
ImageSource::Texture(texture) => Some(texture.size),
ImageSource::Uri(_) | ImageSource::Bytes(_, _) => None,
}
}

/// # Errors
/// Failure to load the texture.
pub fn load(
Expand Down
12 changes: 8 additions & 4 deletions crates/egui_demo_app/src/apps/image_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,17 @@ impl eframe::App for ImageViewer {

// bg_fill
ui.add_space(2.0);
ui.label("Background color");
ui.color_edit_button_srgba(&mut self.image_options.bg_fill);
ui.horizontal(|ui| {
ui.color_edit_button_srgba(&mut self.image_options.bg_fill);
ui.label("Background color");
});

// tint
ui.add_space(2.0);
ui.label("Tint");
ui.color_edit_button_srgba(&mut self.image_options.tint);
ui.horizontal(|ui| {
ui.color_edit_button_srgba(&mut self.image_options.tint);
ui.label("Tint");
});

// fit
ui.add_space(10.0);
Expand Down
2 changes: 1 addition & 1 deletion crates/egui_demo_app/src/wrap_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl WrapApp {
),
#[cfg(feature = "image_viewer")]
(
"🖼 Image Viewer",
"🖼 Image Viewer",
Anchor::ImageViewer,
&mut self.state.image_viewer as &mut dyn eframe::App,
),
Expand Down
Loading