image/rendering: Use scale instead of scale-factor
`GdkSurface:scale` supports fractional scaling available as "scale-monitor-framebuffer" on Mutter. Use this value everywhere instead of the integer scale-factor Closing #182
This commit is contained in:
parent
2ef5032627
commit
057ca6a3d0
7 changed files with 119 additions and 82 deletions
|
|
@ -99,7 +99,7 @@ impl Tile {
|
||||||
options: &RenderOptions,
|
options: &RenderOptions,
|
||||||
) {
|
) {
|
||||||
// Multiply by scale factor to get the image scaled to `zoom` in physical pixels
|
// Multiply by scale factor to get the image scaled to `zoom` in physical pixels
|
||||||
let zoom = output_zoom as f32 * options.scale_factor as f32 / self.zoom();
|
let zoom = output_zoom as f32 * options.scaling as f32 / self.zoom();
|
||||||
let area = round(&self.area().scale(zoom, zoom));
|
let area = round(&self.area().scale(zoom, zoom));
|
||||||
let area_with_bleed = round(&self.area_with_bleed().scale(zoom, zoom));
|
let area_with_bleed = round(&self.area_with_bleed().scale(zoom, zoom));
|
||||||
|
|
||||||
|
|
@ -214,7 +214,7 @@ pub struct TileLayer {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RenderOptions {
|
pub struct RenderOptions {
|
||||||
pub scaling_filter: gsk::ScalingFilter,
|
pub scaling_filter: gsk::ScalingFilter,
|
||||||
pub scale_factor: i32,
|
pub scaling: f64,
|
||||||
pub background_color: Option<gdk::RGBA>,
|
pub background_color: Option<gdk::RGBA>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,16 +231,13 @@ impl TiledImage {
|
||||||
log::trace!("Rendering {} tiles", tiles.len());
|
log::trace!("Rendering {} tiles", tiles.len());
|
||||||
|
|
||||||
// Scale from application pixels back to physical pixels
|
// Scale from application pixels back to physical pixels
|
||||||
snapshot.scale(
|
snapshot.scale(1. / options.scaling as f32, 1. / options.scaling as f32);
|
||||||
1. / options.scale_factor as f32,
|
|
||||||
1. / options.scale_factor as f32,
|
|
||||||
);
|
|
||||||
// reverse to put least fitting tiles in the background
|
// reverse to put least fitting tiles in the background
|
||||||
for (_, tile) in tiles.iter().rev() {
|
for (_, tile) in tiles.iter().rev() {
|
||||||
tile.add_to_snapshot(snapshot, zoom, options);
|
tile.add_to_snapshot(snapshot, zoom, options);
|
||||||
}
|
}
|
||||||
// reset scale
|
// reset scale
|
||||||
snapshot.scale(options.scale_factor as f32, options.scale_factor as f32);
|
snapshot.scale(options.scaling as f32, options.scaling as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, tile: Tile) {
|
pub fn push(&mut self, tile: Tile) {
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ use adw::subclass::prelude::*;
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use glib::subclass::Signal;
|
use glib::subclass::Signal;
|
||||||
|
use glib::{Properties, SignalGroup};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use std::cell::{Cell, OnceCell, RefCell};
|
use std::cell::{Cell, OnceCell, RefCell};
|
||||||
|
|
@ -119,7 +120,6 @@ const THUMBNAIL_SIZE: f32 = 128.;
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
use glib::Properties;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Properties)]
|
#[derive(Debug, Default, Properties)]
|
||||||
#[properties(wrapper_type = super::LpImage)]
|
#[properties(wrapper_type = super::LpImage)]
|
||||||
|
|
@ -215,7 +215,8 @@ mod imp {
|
||||||
pub(super) locked_gestured: Cell<Option<Gesture>>,
|
pub(super) locked_gestured: Cell<Option<Gesture>>,
|
||||||
|
|
||||||
pub(super) widget_dimensions: Cell<(i32, i32)>,
|
pub(super) widget_dimensions: Cell<(i32, i32)>,
|
||||||
pub(super) scale_factor: Cell<i32>,
|
pub(super) scaling: Cell<f64>,
|
||||||
|
pub(super) surface_signals: OnceCell<SignalGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
|
@ -256,13 +257,29 @@ mod imp {
|
||||||
self.zoom.set(1.);
|
self.zoom.set(1.);
|
||||||
self.zoom_target.set(1.);
|
self.zoom_target.set(1.);
|
||||||
self.best_fit.set(true);
|
self.best_fit.set(true);
|
||||||
self.scale_factor.set(obj.scale_factor());
|
self.scaling.set(obj.scaling());
|
||||||
|
|
||||||
self.connect_input_handling();
|
self.connect_input_handling();
|
||||||
|
|
||||||
obj.connect_scale_factor_notify(|obj| {
|
let surface_signals = self
|
||||||
obj.queue_resize();
|
.surface_signals
|
||||||
});
|
.get_or_init(SignalGroup::new::<gdk::Surface>);
|
||||||
|
|
||||||
|
surface_signals.connect_notify_local(
|
||||||
|
Some("scale"),
|
||||||
|
glib::clone!(@weak obj => move |_, _| {
|
||||||
|
log::debug!("Scale changed signal");
|
||||||
|
obj.queue_resize();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
obj.connect_realize(glib::clone!(@weak surface_signals => move |obj| {
|
||||||
|
surface_signals.set_target(obj.native().map(|x| x.surface()).as_ref());
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_unrealize(glib::clone!(@weak surface_signals => move |_| {
|
||||||
|
surface_signals.set_target(gdk::Surface::NONE);
|
||||||
|
}));
|
||||||
|
|
||||||
adw::StyleManager::default().connect_dark_notify(glib::clone!(@weak obj => move |_| {
|
adw::StyleManager::default().connect_dark_notify(glib::clone!(@weak obj => move |_| {
|
||||||
glib::spawn_future_local(async move {
|
glib::spawn_future_local(async move {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ impl LpImage {
|
||||||
let scale = f32::min(1., THUMBNAIL_SIZE / long_side as f32);
|
let scale = f32::min(1., THUMBNAIL_SIZE / long_side as f32);
|
||||||
let render_options = tiling::RenderOptions {
|
let render_options = tiling::RenderOptions {
|
||||||
scaling_filter: gsk::ScalingFilter::Trilinear,
|
scaling_filter: gsk::ScalingFilter::Trilinear,
|
||||||
scale_factor: self.scale_factor(),
|
scaling: self.scaling(),
|
||||||
background_color: Some(self.background_color()),
|
background_color: Some(self.background_color()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ impl LpImage {
|
||||||
let render_options = tiling::RenderOptions {
|
let render_options = tiling::RenderOptions {
|
||||||
scaling_filter: gsk::ScalingFilter::Nearest,
|
scaling_filter: gsk::ScalingFilter::Nearest,
|
||||||
background_color: None,
|
background_color: None,
|
||||||
scale_factor: 1,
|
scaling: 1.,
|
||||||
};
|
};
|
||||||
self.imp()
|
self.imp()
|
||||||
.frame_buffer
|
.frame_buffer
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ impl LpImage {
|
||||||
let render_options = tiling::RenderOptions {
|
let render_options = tiling::RenderOptions {
|
||||||
scaling_filter: gsk::ScalingFilter::Trilinear,
|
scaling_filter: gsk::ScalingFilter::Trilinear,
|
||||||
background_color: None,
|
background_color: None,
|
||||||
scale_factor: 1,
|
scaling: 1.,
|
||||||
};
|
};
|
||||||
|
|
||||||
let snapshot = gtk::Snapshot::new();
|
let snapshot = gtk::Snapshot::new();
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ impl WidgetImpl for imp::LpImage {
|
||||||
fn size_allocate(&self, width: i32, height: i32, _baseline: i32) {
|
fn size_allocate(&self, width: i32, height: i32, _baseline: i32) {
|
||||||
let obj = self.obj();
|
let obj = self.obj();
|
||||||
|
|
||||||
let (scale_changed, scale_change) = if obj.scale_factor() != self.scale_factor.get() {
|
let (scale_changed, scale_change) = if obj.scaling() != self.scaling.get() {
|
||||||
let scale_change = obj.scale_factor() as f64 / self.scale_factor.get() as f64;
|
let scale_change = obj.scaling() / self.scaling.get();
|
||||||
self.scale_factor.set(obj.scale_factor());
|
self.scaling.set(obj.scaling());
|
||||||
(true, scale_change)
|
(true, scale_change)
|
||||||
} else {
|
} else {
|
||||||
(false, 1.)
|
(false, 1.)
|
||||||
|
|
@ -78,7 +78,7 @@ impl WidgetImpl for imp::LpImage {
|
||||||
|
|
||||||
let render_options = tiling::RenderOptions {
|
let render_options = tiling::RenderOptions {
|
||||||
scaling_filter,
|
scaling_filter,
|
||||||
scale_factor: obj.scale_factor(),
|
scaling: obj.scaling(),
|
||||||
background_color: Some(obj.background_color()),
|
background_color: Some(obj.background_color()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -128,65 +128,54 @@ impl WidgetImpl for imp::LpImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) {
|
fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) {
|
||||||
let (image_width, image_height) = self.obj().image_size();
|
let obj = self.obj();
|
||||||
|
let (image_width, image_height) = obj.image_size();
|
||||||
|
|
||||||
if image_width > 0 && image_height > 0 {
|
if image_width > 0 && image_height > 0 {
|
||||||
if let Some(display) = gdk::Display::default() {
|
if let Some((monitor_width, monitor_height)) = obj.monitor_size() {
|
||||||
if let Some(native) = self.obj().native() {
|
let hidpi_scale = self.obj().scaling();
|
||||||
if let Some(monitor) = display.monitor_at_surface(&native.surface()) {
|
|
||||||
let hidpi_scale = self.obj().scale_factor() as f64;
|
|
||||||
|
|
||||||
let monitor_geometry = monitor.geometry();
|
// areas
|
||||||
// TODO: Per documentation those dimensions should not be physical
|
let monitor_area = monitor_width * monitor_height;
|
||||||
// pixels. But on Wayland they are physical
|
let image_area = image_width as f64 * image_height as f64;
|
||||||
// pixels and on X11 not. Taking the version
|
|
||||||
// that works on Wayland for now. <https://gitlab.gnome.org/GNOME/gtk/-/issues/5391>
|
|
||||||
let monitor_width = monitor_geometry.width() as f64 - 40.;
|
|
||||||
let monitor_height = monitor_geometry.height() as f64 - 60.;
|
|
||||||
|
|
||||||
// areas
|
let occupy_area_factor = if monitor_area < 1024. * 768. {
|
||||||
let monitor_area = monitor_width * monitor_height;
|
// for small monitors occupy 80% of the area
|
||||||
let image_area = image_width as f64 * image_height as f64;
|
0.8
|
||||||
|
} else {
|
||||||
|
// for large monitors occupy 30% of the area
|
||||||
|
0.3
|
||||||
|
};
|
||||||
|
|
||||||
let occupy_area_factor = if monitor_area < 1024. * 768. {
|
// factor for width and height that will achieve the desired area
|
||||||
// for small monitors occupy 80% of the area
|
// occupation derived from:
|
||||||
0.8
|
// monitor_area * occupy_area_factor ==
|
||||||
} else {
|
// (image_width * size_scale) * (image_height * size_scale)
|
||||||
// for large monitors occupy 30% of the area
|
let size_scale = f64::sqrt(monitor_area / image_area * occupy_area_factor);
|
||||||
0.3
|
// ensure that we never increase image size
|
||||||
};
|
let target_scale = f64::min(1.0, size_scale);
|
||||||
|
let mut nat_width = image_width as f64 * target_scale;
|
||||||
|
let mut nat_height = image_height as f64 * target_scale;
|
||||||
|
|
||||||
// factor for width and height that will achieve the desired area
|
// scale down if targeted occupation does not fit in one direction
|
||||||
// occupation derived from:
|
if nat_width > monitor_width {
|
||||||
// monitor_area * occupy_area_factor ==
|
nat_width = monitor_width;
|
||||||
// (image_width * size_scale) * (image_height * size_scale)
|
nat_height = nat_height * monitor_width / nat_width;
|
||||||
let size_scale = f64::sqrt(monitor_area / image_area * occupy_area_factor);
|
|
||||||
// ensure that we never increase image size
|
|
||||||
let target_scale = f64::min(1.0, size_scale);
|
|
||||||
let mut nat_width = image_width as f64 * target_scale;
|
|
||||||
let mut nat_height = image_height as f64 * target_scale;
|
|
||||||
|
|
||||||
// scale down if targeted occupation does not fit in one direction
|
|
||||||
if nat_width > monitor_width {
|
|
||||||
nat_width = monitor_width;
|
|
||||||
nat_height = nat_height * monitor_width / nat_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
// same for other direction
|
|
||||||
if nat_height > monitor_height {
|
|
||||||
nat_height = monitor_height;
|
|
||||||
nat_width = nat_width * monitor_height / nat_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
let size = match orientation {
|
|
||||||
gtk::Orientation::Horizontal => (nat_width / hidpi_scale).round(),
|
|
||||||
gtk::Orientation::Vertical => (nat_height / hidpi_scale).round(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (0, size as i32, -1, -1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// same for other direction
|
||||||
|
if nat_height > monitor_height {
|
||||||
|
nat_height = monitor_height;
|
||||||
|
nat_width = nat_width * monitor_height / nat_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = match orientation {
|
||||||
|
gtk::Orientation::Horizontal => (nat_width / hidpi_scale).round(),
|
||||||
|
gtk::Orientation::Vertical => (nat_height / hidpi_scale).round(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (0, size as i32, -1, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,11 +236,11 @@ impl LpImage {
|
||||||
|
|
||||||
/// Returns the area of the image that is visible in physical pixels
|
/// Returns the area of the image that is visible in physical pixels
|
||||||
pub(super) fn viewport(&self) -> graphene::Rect {
|
pub(super) fn viewport(&self) -> graphene::Rect {
|
||||||
let scale_factor = self.scale_factor() as f32;
|
let scaling = self.scaling() as f32;
|
||||||
let x = self.hadjustment().value() as f32 * scale_factor;
|
let x = self.hadjustment().value() as f32 * scaling;
|
||||||
let y = self.vadjustment().value() as f32 * scale_factor;
|
let y = self.vadjustment().value() as f32 * scaling;
|
||||||
let width = self.width() as f32 * scale_factor;
|
let width = self.width() as f32 * scaling;
|
||||||
let height = self.height() as f32 * scale_factor;
|
let height = self.height() as f32 * scaling;
|
||||||
|
|
||||||
graphene::Rect::new(x, y, width, height)
|
graphene::Rect::new(x, y, width, height)
|
||||||
}
|
}
|
||||||
|
|
@ -273,7 +262,7 @@ impl LpImage {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let scale = self.scale_factor() as f64;
|
let scale = self.scaling();
|
||||||
(number * scale).round() / scale
|
(number * scale).round() / scale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +271,41 @@ impl LpImage {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let scale = self.scale_factor() as f32;
|
let scale = self.scaling() as f32;
|
||||||
(number * scale).round() / scale
|
(number * scale).round() / scale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scaling(&self) -> f64 {
|
||||||
|
self.native()
|
||||||
|
.map(|x| x.surface().scale())
|
||||||
|
.unwrap_or_else(|| self.scale_factor() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Monitor size in physical pixels
|
||||||
|
pub fn monitor_size(&self) -> Option<(f64, f64)> {
|
||||||
|
if let Some(display) = gdk::Display::default() {
|
||||||
|
if let Some(native) = self.native() {
|
||||||
|
if let Some(monitor) = display.monitor_at_surface(&native.surface()) {
|
||||||
|
let hidpi_scale = self.scaling();
|
||||||
|
let monitor_geometry = monitor.geometry();
|
||||||
|
let monitor_size = (
|
||||||
|
monitor_geometry.width() as f64,
|
||||||
|
monitor_geometry.height() as f64,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assume scale-monitor-framebuffer disabled for non-fractional scaling
|
||||||
|
// <https://gitlab.gnome.org/GNOME/gtk/-/issues/5391>
|
||||||
|
let physical_geometry = if hidpi_scale == self.scale_factor() as f64 {
|
||||||
|
monitor_size
|
||||||
|
} else {
|
||||||
|
(monitor_size.0 * hidpi_scale, monitor_size.1 * hidpi_scale)
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(physical_geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ impl LpImage {
|
||||||
pub(super) fn zoom_level_best_fit_for_rotation(&self, rotation: f64) -> f64 {
|
pub(super) fn zoom_level_best_fit_for_rotation(&self, rotation: f64) -> f64 {
|
||||||
let rotated = rotation.to_radians().sin().abs();
|
let rotated = rotation.to_radians().sin().abs();
|
||||||
let (image_width, image_height) = (
|
let (image_width, image_height) = (
|
||||||
self.original_dimensions().0 as f64 / self.scale_factor() as f64,
|
self.original_dimensions().0 as f64 / self.scaling(),
|
||||||
self.original_dimensions().1 as f64 / self.scale_factor() as f64,
|
self.original_dimensions().1 as f64 / self.scaling(),
|
||||||
);
|
);
|
||||||
let texture_aspect_ratio = image_width / image_height;
|
let texture_aspect_ratio = image_width / image_height;
|
||||||
let widget_aspect_ratio = self.width() as f64 / self.height() as f64;
|
let widget_aspect_ratio = self.width() as f64 / self.height() as f64;
|
||||||
|
|
@ -81,7 +81,7 @@ impl LpImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn applicable_zoom(&self) -> f64 {
|
pub(super) fn applicable_zoom(&self) -> f64 {
|
||||||
decoder::tiling::zoom_normalize(self.zoom()) / self.scale_factor() as f64
|
decoder::tiling::zoom_normalize(self.zoom()) / self.scaling()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maximal zoom allowed for this image
|
/// Maximal zoom allowed for this image
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue