diff --git a/news.d/47.rc/fixed-avoid-flickering-when-reloading-image b/news.d/47.rc/fixed-avoid-flickering-when-reloading-image new file mode 100644 index 0000000..246920c --- /dev/null +++ b/news.d/47.rc/fixed-avoid-flickering-when-reloading-image @@ -0,0 +1 @@ +Flickering while images are reloaded. Instead, keep the old textures around until a new texture is available. diff --git a/src/decoder/tiling.rs b/src/decoder/tiling.rs index 55185b1..095f039 100644 --- a/src/decoder/tiling.rs +++ b/src/decoder/tiling.rs @@ -177,19 +177,18 @@ impl FrameBuffer { self.current().cleanup(zoom, viewport); } - /// Delete all stored information - /// - /// Used when reloading image - pub fn reset(&mut self) { - self.update_sender = None; - self.images.clear(); - } - pub fn contains(&self, zoom: f64, coordinates: Coordinates) -> bool { self.images .front() .map_or(false, |x| x.contains(zoom, coordinates)) } + + /// Returns true if there are no textures + pub fn is_empty(&self) -> bool { + self.images + .front() + .map_or(false, |x| x.tile_layers.is_empty()) + } } #[derive(Clone, Debug, Default)] @@ -623,12 +622,15 @@ impl SharedFrameBuffer { }); } - pub fn reset(&self) { - self.buffer.rcu(|tiling_store| { - let mut new_store = (**tiling_store).clone(); - new_store.reset(); - Arc::new(new_store) - }); + /// Reset to default and return old store + /// + /// Used when reloading images + pub fn reset(&self) -> Self { + let old_buffer = self.buffer.swap(Default::default()); + + Self { + buffer: ArcSwap::new(old_buffer), + } } pub fn get_layer_tiling_or_default(&self, zoom: f64, viewport: graphene::Rect) -> Tiling { diff --git a/src/widgets/image.rs b/src/widgets/image.rs index a4d4c54..dee368e 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -163,6 +163,7 @@ mod imp { /// Track changes to this image pub(super) file_monitor: RefCell>, pub(super) frame_buffer: Arc, + pub(super) previous_frame_buffer: SharedFrameBuffer, pub(super) decoder: RefCell>>, /// Rotation final value (can differ from `rotation` during animation) diff --git a/src/widgets/image/loading.rs b/src/widgets/image/loading.rs index 9a13978..b7a6d75 100644 --- a/src/widgets/image/loading.rs +++ b/src/widgets/image/loading.rs @@ -89,6 +89,7 @@ impl imp::LpImage { } DecoderUpdate::Redraw => { self.set_loaded(true); + self.previous_frame_buffer.reset(); obj.queue_draw(); self.frame_buffer.rcu(|tiles| { @@ -313,8 +314,13 @@ impl LpImage { self.imp().set_file_loaded(file).await; let tiles = &self.imp().frame_buffer; + // Delete all stored textures for reloads - tiles.reset(); + let previous_frame_buffer = tiles.reset(); + // Store previos frames to show until new texture is loaded + self.imp() + .previous_frame_buffer + .swap(previous_frame_buffer.load_full()); // Reset background color for reloads imp.set_background_color(None); diff --git a/src/widgets/image/rendering.rs b/src/widgets/image/rendering.rs index 8e3d3b9..2874f0f 100644 --- a/src/widgets/image/rendering.rs +++ b/src/widgets/image/rendering.rs @@ -126,9 +126,17 @@ impl WidgetImpl for imp::LpImage { ); // Add texture(s) - self.frame_buffer - .load() - .add_to_snapshot(snapshot, applicable_zoom, &render_options); + 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 + }; + + use_frame_buffer.add_to_snapshot(snapshot, applicable_zoom, &render_options); snapshot.restore(); diff --git a/src/widgets/image/rotation.rs b/src/widgets/image/rotation.rs index 3b2b4a7..c0867b9 100644 --- a/src/widgets/image/rotation.rs +++ b/src/widgets/image/rotation.rs @@ -97,8 +97,8 @@ impl LpImage { if self.is_best_fit() { let zoom_animation = imp.zoom_animation(); - zoom_animation.set_value_from(dbg!(self.zoom())); - zoom_animation.set_value_to(dbg!(imp.zoom_level_best_fit_for_rotation(target.get()))); + zoom_animation.set_value_from(self.zoom()); + zoom_animation.set_value_to(imp.zoom_level_best_fit_for_rotation(target.get())); zoom_animation.play(); }