#716 store full size img, add zoom & pan

This commit is contained in:
jglad
2025-03-11 21:47:52 +01:00
parent 02ec025096
commit a124187db6
3 changed files with 128 additions and 24 deletions

View File

@@ -119,9 +119,8 @@ fn process_pfp_bitmap(imgtyp: ImageType, mut image: image::DynamicImage) -> Colo
puffin::profile_function!(); puffin::profile_function!();
match imgtyp { match imgtyp {
ImageType::Content(w, h) => { ImageType::Content => {
let image = image.resize(w, h, FilterType::CatmullRom); // DynamicImage let image_buffer = image.clone().into_rgba8();
let image_buffer = image.into_rgba8(); // RgbaImage (ImageBuffer)
let color_image = ColorImage::from_rgba_unmultiplied( let color_image = ColorImage::from_rgba_unmultiplied(
[ [
image_buffer.width() as usize, image_buffer.width() as usize,
@@ -164,7 +163,7 @@ fn parse_img_response(response: ehttp::Response, imgtyp: ImageType) -> Result<Co
let content_type = response.content_type().unwrap_or_default(); let content_type = response.content_type().unwrap_or_default();
let size_hint = match imgtyp { let size_hint = match imgtyp {
ImageType::Profile(size) => SizeHint::Size(size, size), ImageType::Profile(size) => SizeHint::Size(size, size),
ImageType::Content(w, h) => SizeHint::Size(w, h), ImageType::Content => SizeHint::default(),
}; };
if content_type.starts_with("image/svg") { if content_type.starts_with("image/svg") {
@@ -354,8 +353,8 @@ pub fn fetch_binary_from_disk(path: PathBuf) -> Result<Vec<u8>> {
pub enum ImageType { pub enum ImageType {
/// Profile Image (size) /// Profile Image (size)
Profile(u32), Profile(u32),
/// Content Image (width, height) /// Content Image
Content(u32, u32), Content,
} }
pub fn fetch_img( pub fn fetch_img(

View File

@@ -319,7 +319,7 @@ fn image_carousel(
ui, ui,
img_cache, img_cache,
&image, &image,
ImageType::Content(width.round() as u32, height.round() as u32), ImageType::Content,
cache_type.clone(), cache_type.clone(),
|ui| { |ui| {
ui.allocate_space(egui::vec2(spinsz, spinsz)); ui.allocate_space(egui::vec2(spinsz, spinsz));
@@ -370,17 +370,15 @@ fn image_carousel(
let cache_type = current_image.clone().1; let cache_type = current_image.clone().1;
Window::new("image_popup") Window::new("image_popup")
.order(egui::Order::Foreground)
.title_bar(false) .title_bar(false)
.fixed_size(ui.ctx().screen_rect().size()) .fixed_size(ui.ctx().screen_rect().size())
.fixed_pos(ui.ctx().screen_rect().min)
.frame(egui::Frame::none()) .frame(egui::Frame::none())
.show(ui.ctx(), |ui| { .show(ui.ctx(), |ui| {
let screen_rect = ui.ctx().screen_rect(); let screen_rect = ui.ctx().screen_rect();
// escape // escape
if ui.input(|i| i.key_pressed(egui::Key::Escape)) if ui.input(|i| i.key_pressed(egui::Key::Escape)) {
|| (ui.input(|i| i.pointer.any_click()))
{
ui.ctx().memory_mut(|mem| { ui.ctx().memory_mut(|mem| {
mem.data.insert_temp(carousel_id.with("show_popup"), false); mem.data.insert_temp(carousel_id.with("show_popup"), false);
}); });
@@ -390,12 +388,43 @@ fn image_carousel(
ui.painter() ui.painter()
.rect_filled(screen_rect, 0.0, Color32::from_black_alpha(230)); .rect_filled(screen_rect, 0.0, Color32::from_black_alpha(230));
ui.vertical_centered(|ui| { // zoom init
let zoom_id = carousel_id.with("zoom_level");
let mut zoom = ui
.ctx()
.memory(|mem| mem.data.get_temp(zoom_id).unwrap_or(1.0_f32));
// pan init
let pan_id = carousel_id.with("pan_offset");
let mut pan_offset = ui
.ctx()
.memory(|mem| mem.data.get_temp(pan_id).unwrap_or(egui::Vec2::ZERO));
// zoom & scroll
if ui.input(|i| i.pointer.hover_pos()).is_some() {
let scroll_delta = ui.input(|i| i.smooth_scroll_delta);
if scroll_delta.y != 0.0 {
let zoom_factor = if scroll_delta.y > 0.0 { 1.05 } else { 0.95 };
zoom *= zoom_factor;
zoom = zoom.clamp(0.1, 5.0);
if zoom <= 1.0 {
pan_offset = egui::Vec2::ZERO;
}
ui.ctx().memory_mut(|mem| {
mem.data.insert_temp(zoom_id, zoom);
mem.data.insert_temp(pan_id, pan_offset);
});
}
}
ui.centered_and_justified(|ui| {
render_images( render_images(
ui, ui,
img_cache, img_cache,
&image, &image,
ImageType::Content(width.round() as u32, height.round() as u32), ImageType::Content,
cache_type.clone(), cache_type.clone(),
|ui| { |ui| {
ui.allocate_space(egui::vec2(spinsz, spinsz)); ui.allocate_space(egui::vec2(spinsz, spinsz));
@@ -409,23 +438,99 @@ fn image_carousel(
retrieve_latest_texture(&image, gifs, renderable_media), retrieve_latest_texture(&image, gifs, renderable_media),
); );
// top margin because ui.vertical_centered pushes the img to the top let texture_size = texture.size_vec2();
// and ui.centered_and_justified takes up all the screen let screen_size = screen_rect.size();
ui.add_space((screen_rect.height() - texture.size_vec2().y) / 2.0); let scale = (screen_size.x / texture_size.x)
.min(screen_size.y / texture_size.y)
.min(1.0);
let scaled_size = texture_size * scale * zoom;
let img_resp = ui.add( let visible_width = scaled_size.x.min(screen_size.x);
Image::new(texture) let visible_height = scaled_size.y.min(screen_size.y);
.fit_to_original_size(1.0)
.sense(Sense::click()), let max_pan_x = ((scaled_size.x - visible_width) / 2.0).max(0.0);
let max_pan_y = ((scaled_size.y - visible_height) / 2.0).max(0.0);
if max_pan_x > 0.0 {
pan_offset.x = pan_offset.x.clamp(-max_pan_x, max_pan_x);
} else {
pan_offset.x = 0.0;
}
if max_pan_y > 0.0 {
pan_offset.y = pan_offset.y.clamp(-max_pan_y, max_pan_y);
} else {
pan_offset.y = 0.0;
}
let (rect, response) = ui.allocate_exact_size(
egui::vec2(visible_width, visible_height),
egui::Sense::click_and_drag(),
); );
if img_resp.clicked() || img_resp.secondary_clicked() { let uv_min = egui::pos2(
ui.memory_mut(|mem| { 0.5 - (visible_width / scaled_size.x) / 2.0
+ pan_offset.x / scaled_size.x,
0.5 - (visible_height / scaled_size.y) / 2.0
+ pan_offset.y / scaled_size.y,
);
let uv_max = egui::pos2(
uv_min.x + visible_width / scaled_size.x,
uv_min.y + visible_height / scaled_size.y,
);
let uv = egui::Rect::from_min_max(uv_min, uv_max);
ui.painter()
.image(texture.id(), rect, uv, egui::Color32::WHITE);
let img_rect = ui.allocate_rect(rect, Sense::click());
if img_rect.clicked() {
ui.ctx().memory_mut(|mem| {
mem.data.insert_temp(carousel_id.with("show_popup"), true); mem.data.insert_temp(carousel_id.with("show_popup"), true);
}); });
} else if img_rect.clicked_elsewhere() {
ui.ctx().memory_mut(|mem| {
mem.data.insert_temp(carousel_id.with("show_popup"), false);
});
} }
copy_link(url, img_resp); // Handle dragging for pan
if response.dragged() {
let delta = response.drag_delta();
pan_offset.x -= delta.x;
pan_offset.y -= delta.y;
if max_pan_x > 0.0 {
pan_offset.x = pan_offset.x.clamp(-max_pan_x, max_pan_x);
} else {
pan_offset.x = 0.0;
}
if max_pan_y > 0.0 {
pan_offset.y = pan_offset.y.clamp(-max_pan_y, max_pan_y);
} else {
pan_offset.y = 0.0;
}
ui.ctx().memory_mut(|mem| {
mem.data.insert_temp(pan_id, pan_offset);
});
}
// reset zoom on double-click
if response.double_clicked() {
pan_offset = egui::Vec2::ZERO;
zoom = 1.0;
ui.ctx().memory_mut(|mem| {
mem.data.insert_temp(pan_id, pan_offset);
mem.data.insert_temp(zoom_id, zoom);
});
}
copy_link(url, response);
}, },
); );
}); });

View File

@@ -410,7 +410,7 @@ impl<'a> PostView<'a> {
ui, ui,
self.img_cache, self.img_cache,
&media.url, &media.url,
crate::images::ImageType::Content(width, height), crate::images::ImageType::Content,
cache_type, cache_type,
|ui| { |ui| {
ui.spinner(); ui.spinner();