From 56597071322a0b92dedf6a0a6aa52985460bd25f Mon Sep 17 00:00:00 2001 From: Julian Mutter Date: Wed, 22 Nov 2023 21:45:00 +0100 Subject: [PATCH] Make render-caching work!!! --- src/cache.rs | 47 ++++++++---------- src/draw.rs | 52 ++++++------------- src/ui.rs | 138 +++++++++++++++++++++++++-------------------------- 3 files changed, 104 insertions(+), 133 deletions(-) diff --git a/src/cache.rs b/src/cache.rs index 1f1f553..03be594 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,11 +1,12 @@ use cairo::ImageSurface; +use glib::{timeout_future, timeout_future_seconds, ControlFlow}; use gtk::gdk::Texture; use poppler::{Document, Page}; use std::{ collections::BTreeMap, path::{Path, PathBuf}, rc::Rc, - time::Instant, + time::{Duration, Instant}, }; use async_channel::Sender; @@ -34,7 +35,7 @@ impl PageCache { self.pages.get(&page_number).map(Rc::clone) } - pub fn cache_pages(&mut self, page_numbers: Vec) { + pub fn cache_pages(&mut self, page_numbers: Vec, area_height: i32) { println!("Caching pages {:?}", page_numbers); let begin_of_cashing = Instant::now(); for page_number in page_numbers { @@ -44,7 +45,7 @@ impl PageCache { if let Some(page) = self.document.page(page_number as i32) { let pages = vec![Rc::new(page)]; - let texture = draw::draw_pages_to_texture(&pages, 500, 500); + let texture = draw::draw_pages_to_texture(&pages, area_height); self.pages.insert(page_number, Rc::new(texture)); @@ -74,11 +75,11 @@ impl PageCache { Ok(()) } - async fn process_command(&mut self, command: CacheCommand) -> Option { + fn process_command(&mut self, command: CacheCommand) -> Option { println!("Processing command: {:?}...", command); match command { - CacheCommand::CachePages { pages } => { - self.cache_pages(pages); + CacheCommand::CachePages { pages, area_height } => { + self.cache_pages(pages, area_height); None } CacheCommand::GetCurrentTwoPages { page_left_number } => { @@ -106,14 +107,16 @@ impl PageCache { #[derive(Debug)] pub enum CacheCommand { - CachePages { pages: Vec }, - GetCurrentTwoPages { page_left_number: PageNumber }, + CachePages { + pages: Vec, + area_height: i32, + }, + GetCurrentTwoPages { + page_left_number: PageNumber, + }, } pub enum CacheResponse { - DocumentLoaded { - num_pages: usize, - }, SinglePageRetrieved { page: Rc, }, @@ -123,35 +126,25 @@ pub enum CacheResponse { }, } -pub fn spawn_async_cache(file: impl AsRef, receiver: F) -> Sender +pub fn spawn_async_cache(document: Document, receiver: F) -> Sender where F: Fn(CacheResponse) + 'static, { let (command_sender, command_receiver) = async_channel::unbounded(); - let path: PathBuf = file.as_ref().to_path_buf(); + let mut cache = PageCache::new(document, 10); glib::spawn_future_local(async move { - println!("async loading of document:..."); - - let uri = format!("file://{}", path.to_str().unwrap()); - let document = poppler::Document::from_file(&uri, None).unwrap(); - let num_pages = document.n_pages() as usize; - receiver(CacheResponse::DocumentLoaded { num_pages }); - - let mut cache = PageCache::new(document, 10); - while let Ok(command) = command_receiver.recv().await { - // if !command_receiver.is_empty() { - // // ignore command if more up to date ones are available - // continue; - // } - if let Some(response) = cache.process_command(command).await { + if let Some(response) = cache.process_command(command) { // response_sender.send_blocking(response).unwrap(); println!("Command processed, activating receiver...."); receiver(response); println!("receiver done"); } + + // Add delay to tell gtk to give rendering priority + timeout_future(Duration::from_millis(1)).await; } }); diff --git a/src/draw.rs b/src/draw.rs index 4a296bc..0419e9b 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -9,11 +9,16 @@ use gtk::{ }; use poppler::Page; -use crate::ui::DocumentCanvas; +pub fn draw_pages_to_texture(pages: &[Rc], area_height: i32) -> Texture { + let area_height = i32::max(400, area_height); + let total_width_normalized: f64 = pages + .iter() + .map(|page| page.size()) + .map(|(w, h)| w / h) + .sum(); + let area_width = (total_width_normalized * area_height as f64 + 0.5) as i32; -pub fn draw_pages_to_texture(pages: &[Rc], area_width: i32, area_height: i32) -> Texture { - let surface = - ImageSurface::create(cairo::Format::Rgb24, area_width as i32, area_height as i32).unwrap(); + let surface = ImageSurface::create(cairo::Format::Rgb24, area_width, area_height).unwrap(); let context = Context::new(&surface).unwrap(); draw_pages(pages, &context, area_width, area_height); @@ -22,37 +27,6 @@ pub fn draw_pages_to_texture(pages: &[Rc], area_width: i32, area_height: i Texture::from_bytes(&Bytes::from(&stream)).unwrap() } -pub fn draw( - document_canvas: &Option, - context: &Context, - area_width: i32, - area_height: i32, -) { - println!("Draw"); - if let Some(document_canvas) = document_canvas { - let begin_of_drawing = Instant::now(); - if document_canvas.num_pages.unwrap_or(0) > 1 { - let mut pages = Vec::new(); - if let Some(page_left) = &document_canvas.left_page { - // context - // .set_source_surface(page_left.as_ref(), 0.0, 0.0) - // .unwrap(); - pages.push(Rc::clone(page_left)); - } - if let Some(page_right) = &document_canvas.right_page { - pages.push(Rc::clone(page_right)); - } - // draw_pages(&pages, context, area_width, area_height); - } - - println!( - "Finished drawing in {}ms", - begin_of_drawing.elapsed().as_millis() - ); - document_canvas.cache_surrounding_pages(); - } -} - fn draw_pages(pages: &[Rc], context: &Context, area_width: i32, area_height: i32) { if pages.is_empty() { return; @@ -66,7 +40,8 @@ fn draw_pages(pages: &[Rc], context: &Context, area_width: i32, area_heigh .map(|page| page.size()) .map(|(w, h)| w / h) .sum(); - let height_to_scale_to = f64::min(area_width / total_width_normalized, area_height); + // let height_to_scale_to = f64::min(area_width / total_width_normalized, area_height); + let height_to_scale_to = area_height; let total_width = total_width_normalized * height_to_scale_to; context.set_source_rgba(1.0, 1.0, 1.0, 1.0); @@ -81,6 +56,11 @@ fn draw_pages(pages: &[Rc], context: &Context, area_width: i32, area_heigh let scale = height_to_scale_to / page_height; let scaled_width = page_width * scale; + println!( + "drawing with size: {}, {}", + scaled_width, height_to_scale_to + ); + // context.translate(total_width_of_rendered_pages, 0.0); // Poppler sometimes crops white border, draw it manually context.rectangle(0.0, 0.0, scaled_width, height_to_scale_to); diff --git a/src/ui.rs b/src/ui.rs index 6ce0380..59625be 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,4 +1,9 @@ -use std::{cell::RefCell, path::Path, rc::Rc, time::Instant}; +use std::{ + cell::RefCell, + path::{Path, PathBuf}, + rc::Rc, + time::Instant, +}; use async_channel::Sender; use gtk::{ @@ -6,7 +11,7 @@ use gtk::{ gdk::{ffi::gdk_pixbuf_get_from_surface, Texture}, gdk_pixbuf::{ffi::GdkPixbuf, Pixbuf}, glib, Application, ApplicationWindow, Box, Button, DrawingArea, FileChooserAction, - FileChooserDialog, HeaderBar, Image, Label, Orientation, ResponseType, + FileChooserDialog, HeaderBar, Image, Label, Orientation, Picture, ResponseType, }; use crate::{ @@ -21,8 +26,9 @@ pub struct Ui { bottom_bar: gtk::Box, header_bar: gtk::HeaderBar, page_indicator: gtk::Label, - pub drawing_area: gtk::DrawingArea, - pub image: Image, + pub image_container: Box, + pub image_left: Picture, + pub image_right: Picture, pub document_canvas: Option, } @@ -30,8 +36,6 @@ pub struct DocumentCanvas { current_page_number: usize, pub num_pages: Option, page_cache_sender: Sender, - pub left_page: Option>, - pub right_page: Option>, } impl DocumentCanvas { @@ -40,8 +44,6 @@ impl DocumentCanvas { current_page_number: 0, num_pages: None, page_cache_sender, - left_page: None, - right_page: None, } } @@ -57,15 +59,17 @@ impl DocumentCanvas { self.current_page_number = self.current_page_number.saturating_sub(1); } - pub fn cache_initial_pages(&self) { + pub fn cache_initial_pages(&self, area_height: i32) { self.page_cache_sender .send_blocking(CacheCommand::CachePages { pages: vec![self.current_page_number, self.current_page_number + 1], + area_height, }) .unwrap(); } - pub fn cache_surrounding_pages(&self) { + pub fn cache_surrounding_pages(&self, area_height: i32) { + println!("Send cache request"); self.page_cache_sender .send_blocking(CacheCommand::CachePages { pages: vec![ @@ -76,6 +80,7 @@ impl DocumentCanvas { self.current_page_number + 2, self.current_page_number + 3, ], + area_height, }) .unwrap(); } @@ -143,18 +148,18 @@ fn process_left_click(ui: &mut Ui, x: f64, y: f64) { return; } - let center = ui.drawing_area.width() / 2; - if y < (ui.drawing_area.height() / 5) as f64 { + let center = ui.image_container.width() / 2; + if y < (ui.image_container.height() / 5) as f64 { toggle_fullscreen(ui); } else if x > center as f64 { - if x < ui.drawing_area.width() as f64 * 0.75 { + if x < ui.image_container.width() as f64 * 0.75 { ui.document_canvas.as_mut().unwrap().increase_page_number(); } else { ui.document_canvas.as_mut().unwrap().increase_page_number(); ui.document_canvas.as_mut().unwrap().increase_page_number(); } } else if x < center as f64 { - if x > ui.drawing_area.width() as f64 * 0.25 { + if x > ui.image_container.width() as f64 * 0.25 { ui.document_canvas.as_mut().unwrap().decrease_page_number(); } else { ui.document_canvas.as_mut().unwrap().decrease_page_number(); @@ -175,32 +180,48 @@ impl Ui { .title("Music Reader") .child(&app_wrapper) .maximized(true) + .width_request(600) + .height_request(400) .build(); + let image_container = Box::builder() + .spacing(0) + // .width_request(600) + // .height_request(300) + .vexpand(true) + .hexpand(true) + .halign(gtk::Align::Center) + .build(); + let image_left = Picture::builder() + // .width_request(300) + // .height_request(300) + .vexpand(true) + // .hexpand(true) + .build(); + let image_right = Picture::builder() + // .width_request(300) + // .height_request(300) + .vexpand(true) + // .hexpand(true) + .build(); + image_container.append(&image_left); + image_container.append(&image_right); + let ui = Ui { window, bottom_bar: Box::builder().hexpand_set(true).build(), header_bar: HeaderBar::builder().build(), page_indicator: Label::builder().build(), - image: Image::builder() - .width_request(400) - .height_request(300) - .hexpand(true) - .vexpand(true) - .build(), - drawing_area: DrawingArea::builder() - .width_request(400) - .height_request(300) - .hexpand(true) - .vexpand(true) - .build(), + image_container, + image_left, + image_right, document_canvas: None, }; let ui = Rc::new(RefCell::new(ui)); ui.borrow().header_bar.pack_start(&open_file_button); // app_wrapper.prepend(&ui.borrow().drawing_area); - app_wrapper.prepend(&ui.borrow().image); + app_wrapper.prepend(&ui.borrow().image_container); app_wrapper.append(&ui.borrow().bottom_bar); ui.borrow().bottom_bar.append(&ui.borrow().page_indicator); @@ -216,21 +237,8 @@ impl Ui { process_right_click(&mut ui.borrow_mut(), x, y); })); - // ui.borrow().drawing_area.add_controller(click_left); - // ui.borrow().drawing_area.add_controller(click_right); - ui.borrow().image.add_controller(click_left); - ui.borrow().image.add_controller(click_right); - - // ui.borrow().drawing_area.set_draw_func( - // glib::clone!(@weak ui => move |_area, context, w, h| { - // draw::draw(&ui.borrow().document_canvas, context, w, h); - // }), - // ); - // ui.borrow().image.connect_paintable_notify( - // glib::clone!(@weak ui => @default-panic, move |_| { - // ui.borrow().document_canvas.as_ref().unwrap().cache_surrounding_pages(); - // }), - // ); + ui.borrow().image_container.add_controller(click_left); + ui.borrow().image_container.add_controller(click_right); ui.borrow() .window @@ -269,48 +277,38 @@ fn choose_file(ui: Rc>, window: &ApplicationWindow) { pub fn load_document(file: impl AsRef, ui: Rc>) { println!("Loading file..."); // TODO: catch errors, maybe show error dialog + let path: PathBuf = file.as_ref().to_path_buf(); + let uri = format!("file://{}", path.to_str().unwrap()); + let document = poppler::Document::from_file(&uri, None).unwrap(); + let num_pages = document.n_pages() as usize; let sender = cache::spawn_async_cache( - file, + document, clone!(@weak ui => move |cache_response| match cache_response { - cache::CacheResponse::DocumentLoaded { num_pages } => { - ui.borrow_mut().document_canvas.as_mut().unwrap().num_pages = Some(num_pages); - update_page_status(&ui.borrow()) - } cache::CacheResponse::SinglePageRetrieved { page } => { - ui.borrow_mut().document_canvas.as_mut().unwrap().left_page = Some(page); - ui.borrow_mut().document_canvas.as_mut().unwrap().right_page = None; - ui.borrow().drawing_area.queue_draw(); + ui.borrow_mut().image_left.set_paintable(Some(page.as_ref())); + ui.borrow_mut().image_right.set_visible(false); + let area_height = ui.borrow().image_left.height(); + ui.borrow().document_canvas.as_ref().unwrap().cache_surrounding_pages(area_height); } cache::CacheResponse::TwoPagesRetrieved { page_left, page_right, } => { - ui.borrow_mut().document_canvas.as_mut().unwrap().left_page = Some(Rc::clone(&page_left)); - ui.borrow_mut().document_canvas.as_mut().unwrap().right_page = Some(page_right); - // unsafe{ - // let x = Pixbuf::new(); - // gdk_pixbuf_get_from_surface(page_left, 0, 0, page_left.width(), page_left.height()); - // } - // ui.borrow_mut().image.bitstre - // ui.borrow().drawing_area.queue_draw(); - println!("New Draw"); - let begin_of_drawing = Instant::now(); - ui.borrow_mut().image.set_from_paintable(Some(page_left.as_ref())); - ui.borrow().image.queue_draw(); - println!( - "Finished new drawing in {}ms", - begin_of_drawing.elapsed().as_millis() - ); - ui.borrow().document_canvas.as_ref().unwrap().cache_surrounding_pages(); + ui.borrow_mut().image_left.set_paintable(Some(page_left.as_ref())); + ui.borrow_mut().image_right.set_paintable(Some(page_right.as_ref())); + ui.borrow_mut().image_right.set_visible(true); + let area_height = ui.borrow().image_left.height(); + ui.borrow().document_canvas.as_ref().unwrap().cache_surrounding_pages(area_height); + println!("Image size: {}, {}", ui.borrow().image_left.width(), ui.borrow().image_left.height()); } }), ); - println!("Spawned async cache"); + let mut document_canvas = DocumentCanvas::new(sender); + document_canvas.num_pages = Some(num_pages); + document_canvas.cache_initial_pages(ui.borrow().image_left.height()); - let document_canvas = DocumentCanvas::new(sender); - document_canvas.cache_initial_pages(); ui.borrow_mut().document_canvas = Some(document_canvas); update_page_status(&ui.borrow());