editing: Preview cropping/rotate/mirror operations
This commit is contained in:
parent
e9869bcb40
commit
367fdf207d
14 changed files with 419 additions and 36 deletions
|
|
@ -70,7 +70,7 @@ impl imp::LpImage {
|
|||
return Some(Self::default_background_color());
|
||||
}
|
||||
|
||||
let (width, height) = self.original_dimensions();
|
||||
let (width, height) = self.untransformed_dimensions();
|
||||
let max_size = i32::max(width, height);
|
||||
|
||||
// Only use max 200px size scaled image for analysis
|
||||
|
|
|
|||
73
src/widgets/image/editing.rs
Normal file
73
src/widgets/image/editing.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use glycin::Operations;
|
||||
use tiling::RenderOptions;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl imp::LpImage {
|
||||
pub(super) fn apply_operations(
|
||||
&self,
|
||||
node: gsk::RenderNode,
|
||||
snapshot: >k::Snapshot,
|
||||
) -> Result<(), crate::editing::preview::EditingError> {
|
||||
if let Some(operations) = self.operations.borrow().as_ref() {
|
||||
let (width, height) = self.original_dimensions();
|
||||
let new_dimensions = crate::editing::preview::apply_operations(
|
||||
node,
|
||||
width as u32,
|
||||
height as u32,
|
||||
operations,
|
||||
snapshot,
|
||||
self.applicable_zoom(),
|
||||
)?;
|
||||
|
||||
self.overwrite_dimensions.set(Some(new_dimensions));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LpImage {
|
||||
pub fn set_operations(&self, operations: Option<glycin::Operations>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if operations.is_none() {
|
||||
imp.overwrite_dimensions.set(None);
|
||||
}
|
||||
imp.operations.replace(operations);
|
||||
|
||||
let tmp_snapshot = gtk::Snapshot::new();
|
||||
imp.active_frame_buffer().add_to_snapshot(
|
||||
&tmp_snapshot,
|
||||
1.,
|
||||
&RenderOptions {
|
||||
background_color: None,
|
||||
scaling: 1.,
|
||||
scaling_filter: gsk::ScalingFilter::Nearest,
|
||||
},
|
||||
);
|
||||
if let Some(node) = tmp_snapshot.to_node() {
|
||||
imp.apply_operations(node, >k::Snapshot::new());
|
||||
} else {
|
||||
log::error!("Render node is empty");
|
||||
}
|
||||
|
||||
imp.configure_best_fit();
|
||||
self.queue_draw();
|
||||
}
|
||||
|
||||
pub fn add_operation(&self, operation: glycin::Operation) {
|
||||
let imp = self.imp();
|
||||
|
||||
let mut operations = imp
|
||||
.operations
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|x| x.operations().to_vec())
|
||||
.unwrap_or_default();
|
||||
|
||||
operations.push(operation);
|
||||
|
||||
self.set_operations(Some(Operations::new(operations)));
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ impl imp::LpImage {
|
|||
obj.reset_rotation();
|
||||
}
|
||||
DecoderUpdate::Dimensions => {
|
||||
log::debug!("Received dimensions: {:?}", self.original_dimensions());
|
||||
log::debug!("Received dimensions: {:?}", self.untransformed_dimensions());
|
||||
obj.notify_image_size_available();
|
||||
self.configure_best_fit();
|
||||
self.request_tiles();
|
||||
|
|
|
|||
|
|
@ -29,6 +29,14 @@ impl imp::LpImage {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn untransformed_dimensions(&self) -> (i32, i32) {
|
||||
if let Some((width, height)) = self.overwrite_dimensions.get() {
|
||||
(width as i32, height as i32)
|
||||
} else {
|
||||
self.original_dimensions()
|
||||
}
|
||||
}
|
||||
|
||||
/// Image width with current zoom factor and rotation
|
||||
///
|
||||
/// During rotation it is an interpolated size that does not
|
||||
|
|
@ -43,7 +51,7 @@ impl imp::LpImage {
|
|||
}
|
||||
|
||||
pub fn image_width(&self, zoom: f64) -> f64 {
|
||||
let (width, height) = self.original_dimensions();
|
||||
let (width, height) = self.untransformed_dimensions();
|
||||
|
||||
let rotated = self.obj().rotation().to_radians().sin().abs();
|
||||
|
||||
|
|
@ -51,7 +59,7 @@ impl imp::LpImage {
|
|||
}
|
||||
|
||||
pub fn image_height(&self, zoom: f64) -> f64 {
|
||||
let (width, height) = self.original_dimensions();
|
||||
let (width, height) = self.untransformed_dimensions();
|
||||
|
||||
let rotated = self.obj().rotation().to_radians().sin().abs();
|
||||
|
||||
|
|
@ -104,7 +112,7 @@ impl LpImage {
|
|||
let (x, y) = self.imp().original_dimensions();
|
||||
(y, x)
|
||||
} else {
|
||||
self.imp().original_dimensions()
|
||||
self.imp().untransformed_dimensions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use tiling::FrameBuffer;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl WidgetImpl for imp::LpImage {
|
||||
|
|
@ -121,17 +123,21 @@ impl WidgetImpl for imp::LpImage {
|
|||
);
|
||||
|
||||
// Add texture(s)
|
||||
let frame_buffer = self.frame_buffer.load();
|
||||
|
||||
// Don't use current frame buffer if it empty during an image reload
|
||||
let use_frame_buffer = if frame_buffer.is_empty() {
|
||||
// Fallback to previous buffer to show previous image until new one is available
|
||||
self.previous_frame_buffer.load()
|
||||
} else {
|
||||
frame_buffer
|
||||
};
|
||||
let frame_buffer = self.active_frame_buffer();
|
||||
|
||||
use_frame_buffer.add_to_snapshot(snapshot, applicable_zoom, &render_options);
|
||||
if self.operations.borrow().is_some() {
|
||||
let tmp_snapshot = gtk::Snapshot::new();
|
||||
frame_buffer.add_to_snapshot(&tmp_snapshot, applicable_zoom, &render_options);
|
||||
if let Some(node) = tmp_snapshot.to_node() {
|
||||
self.apply_operations(node, snapshot);
|
||||
} else {
|
||||
log::error!("Render node is empty");
|
||||
}
|
||||
} else {
|
||||
frame_buffer.add_to_snapshot(snapshot, applicable_zoom, &render_options);
|
||||
}
|
||||
|
||||
snapshot.restore();
|
||||
|
||||
|
|
@ -230,7 +236,7 @@ impl imp::LpImage {
|
|||
return;
|
||||
}
|
||||
|
||||
let (original_width, original_height) = self.original_dimensions();
|
||||
let (untransformed_width, untransformed_height) = self.untransformed_dimensions();
|
||||
let width = self.image_width(zoom) as f32;
|
||||
let height = self.image_height(zoom) as f32;
|
||||
|
||||
|
|
@ -250,8 +256,8 @@ impl imp::LpImage {
|
|||
// Needed for rotating around the center of the image, and
|
||||
// mirroring the image does not put it to a completely different position.
|
||||
snapshot.translate(&graphene::Point::new(
|
||||
-self.round_f32(original_width as f32 * zoom as f32 / 2.),
|
||||
-self.round_f32(original_height as f32 * zoom as f32 / 2.),
|
||||
-self.round_f32(untransformed_width as f32 * zoom as f32 / 2.),
|
||||
-self.round_f32(untransformed_height as f32 * zoom as f32 / 2.),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -268,6 +274,17 @@ impl imp::LpImage {
|
|||
graphene::Rect::new(x, y, width, height)
|
||||
}
|
||||
|
||||
pub(super) fn active_frame_buffer(&self) -> Arc<FrameBuffer> {
|
||||
let frame_buffer = self.frame_buffer.load_full();
|
||||
|
||||
if frame_buffer.is_empty() {
|
||||
// Fallback to previous buffer to show previous image until new one is available
|
||||
self.previous_frame_buffer.load_full()
|
||||
} else {
|
||||
frame_buffer
|
||||
}
|
||||
}
|
||||
|
||||
pub fn widget_width(&self) -> f64 {
|
||||
self.obj().width() as f64
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ impl imp::LpImage {
|
|||
pub(super) fn zoom_level_best_fit_for_rotation(&self, rotation: f64) -> f64 {
|
||||
let rotated = rotation.to_radians().sin().abs();
|
||||
let (image_width, image_height) = (
|
||||
self.original_dimensions().0 as f64 / self.scaling(),
|
||||
self.original_dimensions().1 as f64 / self.scaling(),
|
||||
self.untransformed_dimensions().0 as f64 / self.scaling(),
|
||||
self.untransformed_dimensions().1 as f64 / self.scaling(),
|
||||
);
|
||||
let texture_aspect_ratio = image_width / image_height;
|
||||
let widget_aspect_ratio = self.widget_width() / self.widget_height();
|
||||
|
|
@ -93,7 +93,7 @@ impl imp::LpImage {
|
|||
let obj = self.obj();
|
||||
|
||||
if obj.metadata().is_svg() {
|
||||
let (width, height) = self.original_dimensions();
|
||||
let (width, height) = self.untransformed_dimensions();
|
||||
// Avoid division by 0
|
||||
let long_side = f64::max(1., i32::max(width, height) as f64);
|
||||
// Limit to maz size supported by rsvg
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue