decoder/tiling: Make shared frame buffer a struct

This commit is contained in:
Sophie Herold 2023-12-26 22:07:46 +01:00
parent 6827a9c176
commit 139bd4ebb2
6 changed files with 52 additions and 68 deletions

View file

@ -16,13 +16,13 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
//! Decode using glycin //! Decode using glycin
use std::sync::Arc; use std::sync::Arc;
use arc_swap::ArcSwap;
use gtk::prelude::*; use gtk::prelude::*;
use super::*; use super::*;
use crate::decoder::tiling::{self, FrameBufferExt}; use crate::decoder::tiling::{self, SharedFrameBuffer};
use crate::deps::*; use crate::deps::*;
use crate::metadata::{ImageFormat, Metadata}; use crate::metadata::{ImageFormat, Metadata};
@ -41,11 +41,7 @@ impl Drop for Glycin {
} }
impl Glycin { impl Glycin {
pub fn new( pub fn new(file: gio::File, updater: UpdateSender, tiles: Arc<SharedFrameBuffer>) -> Self {
file: gio::File,
updater: UpdateSender,
tiles: Arc<ArcSwap<tiling::FrameBuffer>>,
) -> Self {
let cancellable = gio::Cancellable::new(); let cancellable = gio::Cancellable::new();
let cancellable_ = cancellable.clone(); let cancellable_ = cancellable.clone();

View file

@ -16,15 +16,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
//! Decode using librsvg //! Decode using librsvg
use std::sync::Arc; use std::sync::Arc;
use anyhow::Context; use anyhow::Context;
use arc_swap::ArcSwap;
use async_channel as mpsc; use async_channel as mpsc;
use gtk::prelude::*; use gtk::prelude::*;
use super::*; use super::*;
use crate::decoder::tiling::{self, FrameBufferExt}; use crate::decoder::tiling::SharedFrameBuffer;
use crate::decoder::TileRequest; use crate::decoder::TileRequest;
use crate::deps::*; use crate::deps::*;
use crate::metadata::ImageFormat; use crate::metadata::ImageFormat;
@ -61,11 +61,7 @@ impl Drop for Svg {
} }
impl Svg { impl Svg {
pub fn new( pub fn new(file: gio::File, updater: UpdateSender, tiles: Arc<SharedFrameBuffer>) -> Self {
file: gio::File,
updater: UpdateSender,
tiles: Arc<ArcSwap<tiling::FrameBuffer>>,
) -> Self {
let current_request: Arc<std::sync::RwLock<Request>> = Default::default(); let current_request: Arc<std::sync::RwLock<Request>> = Default::default();
let request_store = current_request.clone(); let request_store = current_request.clone();
let cancellable = gio::Cancellable::new(); let cancellable = gio::Cancellable::new();

View file

@ -17,18 +17,18 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
//! Decodes several image formats //! Decodes several image formats
pub mod formats; pub mod formats;
pub mod tiling; pub mod tiling;
use std::sync::Arc; use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use arc_swap::ArcSwap;
use formats::*; use formats::*;
pub use formats::{ImageDimensionDetails, RSVG_MAX_SIZE}; pub use formats::{ImageDimensionDetails, RSVG_MAX_SIZE};
use futures_channel::mpsc; use futures_channel::mpsc;
use tiling::FrameBufferExt;
use self::tiling::SharedFrameBuffer;
use crate::deps::*; use crate::deps::*;
use crate::metadata::{ImageFormat, Metadata}; use crate::metadata::{ImageFormat, Metadata};
@ -117,7 +117,7 @@ impl Decoder {
pub async fn new( pub async fn new(
file: gio::File, file: gio::File,
mime_type: Option<String>, mime_type: Option<String>,
tiles: Arc<ArcSwap<tiling::FrameBuffer>>, tiles: Arc<SharedFrameBuffer>,
) -> (Self, mpsc::UnboundedReceiver<DecoderUpdate>) { ) -> (Self, mpsc::UnboundedReceiver<DecoderUpdate>) {
let (sender, receiver) = mpsc::unbounded(); let (sender, receiver) = mpsc::unbounded();
@ -137,7 +137,7 @@ impl Decoder {
update_sender: UpdateSender, update_sender: UpdateSender,
file: gio::File, file: gio::File,
mime_type: Option<String>, mime_type: Option<String>,
tiles: Arc<ArcSwap<tiling::FrameBuffer>>, tiles: Arc<SharedFrameBuffer>,
) -> FormatDecoder { ) -> FormatDecoder {
if let Some(mime_type) = mime_type { if let Some(mime_type) = mime_type {
// Known things we want to match here are // Known things we want to match here are

View file

@ -572,78 +572,68 @@ impl RectExt for graphene::Rect {
} }
} }
/// We always store [`FrameBuffer`]s in an [`ArcSwap`]. #[derive(Debug, Default)]
/// This trait adds convenience functions for this. pub struct SharedFrameBuffer {
pub trait FrameBufferExt { buffer: ArcSwap<FrameBuffer>,
fn push(&self, tile: Tile);
fn push_tile(&self, tiling: Tiling, position: Coordinates, texture: gdk::Texture);
fn push_frame(&self, tile: Tile, dimensions: Coordinates, delay: Duration);
fn reset(&self);
fn get_layer_tiling_or_default(&self, zoom: f64, viewport: graphene::Rect) -> Tiling;
/// Return true if the next frame should be shown and removes the outdated
/// frame
fn frame_timeout(&self, elapsed: Duration) -> bool;
fn next_frame(&self);
/// Returns the number of currently buffered frames
fn n_frames(&self) -> usize;
fn set_original_dimensions(&self, size: Coordinates);
fn set_original_dimensions_full(
&self,
size: Coordinates,
dimension_details: ImageDimensionDetails,
);
fn set_update_sender(&self, sender: UpdateSender);
} }
impl FrameBufferExt for ArcSwap<FrameBuffer> { impl std::ops::Deref for SharedFrameBuffer {
fn push(&self, tile: Tile) { type Target = ArcSwap<FrameBuffer>;
self.rcu(|tiling_store| {
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
impl SharedFrameBuffer {
pub fn push(&self, tile: Tile) {
self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
new_store.current().push(tile.clone()); new_store.current().push(tile.clone());
Arc::new(new_store) Arc::new(new_store)
}); });
if let Some(updater) = &self.load().update_sender { if let Some(updater) = &self.buffer.load().update_sender {
updater.send(DecoderUpdate::Redraw); updater.send(DecoderUpdate::Redraw);
} }
} }
fn push_tile(&self, tiling: Tiling, position: Coordinates, texture: gdk::Texture) { pub fn push_tile(&self, tiling: Tiling, position: Coordinates, texture: gdk::Texture) {
self.rcu(|tiling_store| { self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
new_store new_store
.current() .current()
.push_tile(tiling, position, texture.clone()); .push_tile(tiling, position, texture.clone());
Arc::new(new_store) Arc::new(new_store)
}); });
if let Some(updater) = &self.load().update_sender { if let Some(updater) = &self.buffer.load().update_sender {
updater.send(DecoderUpdate::Redraw); updater.send(DecoderUpdate::Redraw);
} }
} }
fn push_frame(&self, tile: Tile, dimensions: Coordinates, delay: Duration) { pub fn push_frame(&self, tile: Tile, dimensions: Coordinates, delay: Duration) {
let mut store = TiledImage::default(); let mut store = TiledImage::default();
store.push(tile); store.push(tile);
store.original_dimensions = Some(dimensions); store.original_dimensions = Some(dimensions);
store.delay = delay; store.delay = delay;
self.rcu(|tiling_store| { self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
new_store.images.push_back(store.clone()); new_store.images.push_back(store.clone());
Arc::new(new_store) Arc::new(new_store)
}); });
} }
fn reset(&self) { pub fn reset(&self) {
self.rcu(|tiling_store| { self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
new_store.reset(); new_store.reset();
Arc::new(new_store) Arc::new(new_store)
}); });
} }
fn get_layer_tiling_or_default(&self, zoom: f64, viewport: graphene::Rect) -> Tiling { pub fn get_layer_tiling_or_default(&self, zoom: f64, viewport: graphene::Rect) -> Tiling {
let mut tiling = None; let mut tiling = None;
self.rcu(|tiling_store| { self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
tiling = Some( tiling = Some(
new_store new_store
@ -655,8 +645,11 @@ impl FrameBufferExt for ArcSwap<FrameBuffer> {
tiling.unwrap() tiling.unwrap()
} }
fn frame_timeout(&self, elapsed: Duration) -> bool { /// Return true if the next frame should be shown and removes the outdated
/// frame
pub fn frame_timeout(&self, elapsed: Duration) -> bool {
if self if self
.buffer
.load() .load()
.images .images
.front() .front()
@ -669,40 +662,41 @@ impl FrameBufferExt for ArcSwap<FrameBuffer> {
false false
} }
fn next_frame(&self) { pub fn next_frame(&self) {
self.rcu(|tiling_store| { self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
new_store.images.pop_front(); new_store.images.pop_front();
Arc::new(new_store) Arc::new(new_store)
}); });
} }
fn n_frames(&self) -> usize { /// Returns the number of currently buffered frames
self.load().images.len() pub fn n_frames(&self) -> usize {
self.buffer.load().images.len()
} }
fn set_original_dimensions(&self, size: Coordinates) { pub fn set_original_dimensions(&self, size: Coordinates) {
self.set_original_dimensions_full(size, Default::default()); self.set_original_dimensions_full(size, Default::default());
} }
fn set_original_dimensions_full( pub fn set_original_dimensions_full(
&self, &self,
size: Coordinates, size: Coordinates,
dimension_details: ImageDimensionDetails, dimension_details: ImageDimensionDetails,
) { ) {
self.rcu(|tiling_store| { self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
new_store.current().original_dimensions = Some(size); new_store.current().original_dimensions = Some(size);
Arc::new(new_store) Arc::new(new_store)
}); });
if let Some(updater) = &self.load().update_sender { if let Some(updater) = &self.buffer.load().update_sender {
updater.send(DecoderUpdate::Dimensions(dimension_details)); updater.send(DecoderUpdate::Dimensions(dimension_details));
} }
} }
fn set_update_sender(&self, sender: UpdateSender) { pub fn set_update_sender(&self, sender: UpdateSender) {
self.rcu(|tiling_store| { self.buffer.rcu(|tiling_store| {
let mut new_store = (**tiling_store).clone(); let mut new_store = (**tiling_store).clone();
new_store.set_update_sender(sender.clone()); new_store.set_update_sender(sender.clone());
Arc::new(new_store) Arc::new(new_store)

View file

@ -48,13 +48,11 @@ use std::sync::Arc;
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use arc_swap::ArcSwap;
use futures_lite::StreamExt; use futures_lite::StreamExt;
use glib::subclass::Signal; use glib::subclass::Signal;
use glib::{Properties, SignalGroup}; use glib::{Properties, SignalGroup};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::decoder::tiling::FrameBufferExt;
use crate::decoder::{self, tiling, Decoder, DecoderUpdate}; use crate::decoder::{self, tiling, Decoder, DecoderUpdate};
use crate::deps::*; use crate::deps::*;
use crate::metadata::Metadata; use crate::metadata::Metadata;
@ -126,6 +124,7 @@ pub enum FitMode {
mod imp { mod imp {
use super::*; use super::*;
use crate::decoder::tiling::SharedFrameBuffer;
#[derive(Debug, Default, Properties)] #[derive(Debug, Default, Properties)]
#[properties(wrapper_type = super::LpImage)] #[properties(wrapper_type = super::LpImage)]
@ -148,7 +147,7 @@ mod imp {
/// Track changes to this image /// Track changes to this image
pub(super) file_monitor: RefCell<Option<gio::FileMonitor>>, pub(super) file_monitor: RefCell<Option<gio::FileMonitor>>,
pub(super) frame_buffer: Arc<ArcSwap<tiling::FrameBuffer>>, pub(super) frame_buffer: Arc<SharedFrameBuffer>,
pub(super) decoder: RefCell<Option<Arc<Decoder>>>, pub(super) decoder: RefCell<Option<Arc<Decoder>>>,
/// Rotation final value (can differ from `rotation` during animation) /// Rotation final value (can differ from `rotation` during animation)

View file

@ -23,11 +23,10 @@ use adw::subclass::prelude::*;
use glib::Properties; use glib::Properties;
use gtk::prelude::*; use gtk::prelude::*;
use super::image;
use crate::deps::*; use crate::deps::*;
use crate::widgets::LpPrint; use crate::widgets::LpPrint;
use super::image;
mod imp_page { mod imp_page {
use super::*; use super::*;