From 91d6181317a3d62413c5feb25005142d552916e4 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Thu, 9 Dec 2021 09:45:50 +0100 Subject: [PATCH] Don't invert images in inverted mode Fixes #209. --- src/document/djvu.rs | 6 +++++- src/document/epub/mod.rs | 40 +++++++++++++++++++-------------------- src/document/html/mod.rs | 26 ++++++++++++------------- src/document/mod.rs | 1 + src/document/mupdf_sys.rs | 2 +- src/document/pdf.rs | 31 ++++++++++++++++++++++++++++++ src/view/home/book.rs | 4 ++++ src/view/intermission.rs | 4 ++++ src/view/reader/mod.rs | 33 ++++++++++++++++++++++++++++++-- 9 files changed, 110 insertions(+), 37 deletions(-) diff --git a/src/document/djvu.rs b/src/document/djvu.rs index 8eb2ae7e..ca5c107d 100644 --- a/src/document/djvu.rs +++ b/src/document/djvu.rs @@ -9,7 +9,7 @@ use super::{Document, Location, TextLocation, BoundedText, TocEntry}; use super::{chapter, chapter_relative}; use crate::metadata::TextAlign; use crate::framebuffer::Pixmap; -use crate::geom::{Rectangle, CycleDir}; +use crate::geom::{Rectangle, Boundary, CycleDir}; impl Into for Rectangle { fn into(self) -> DjvuRect { @@ -200,6 +200,10 @@ impl Document for DjvuDocument { } } + fn images(&mut self, _loc: Location) -> Option<(Vec, usize)> { + None + } + fn metadata(&self, key: &str) -> Option { unsafe { let mut exp = ddjvu_document_get_anno(self.doc, 1); diff --git a/src/document/epub/mod.rs b/src/document/epub/mod.rs index 2b8d6bc2..28c5d35f 100644 --- a/src/document/epub/mod.rs +++ b/src/document/epub/mod.rs @@ -10,7 +10,7 @@ use crate::framebuffer::Pixmap; use crate::helpers::{Normalize, decode_entities}; use crate::document::{Document, Location, TextLocation, TocEntry, BoundedText, chapter_from_uri}; use crate::unit::pt_to_px; -use crate::geom::{Rectangle, Edge, CycleDir}; +use crate::geom::{Rectangle, Boundary, Edge, CycleDir}; use super::pdf::PdfOpener; use super::html::dom::{XmlTree, NodeRef}; use super::html::engine::{Page, Engine, ResourceFetcher}; @@ -332,25 +332,6 @@ impl EpubDocument { } } - fn images(&mut self, loc: Location) -> Option<(Vec, usize)> { - if self.spine.is_empty() { - return None; - } - - let offset = self.resolve_location(loc)?; - let (index, start_offset) = self.vertebra_coordinates(offset)?; - let page_index = self.page_index(offset, index, start_offset)?; - - self.cache.get(&index).map(|display_list| { - (display_list[page_index].iter().filter_map(|dc| { - match dc { - DrawCommand::Image(ImageCommand { rect, .. }) => Some(*rect), - _ => None, - } - }).collect(), offset) - }) - } - fn build_display_list(&mut self, index: usize, start_offset: usize) -> Vec { let mut text = String::new(); let mut spine_dir = PathBuf::default(); @@ -857,6 +838,25 @@ impl Document for EpubDocument { }) } + fn images(&mut self, loc: Location) -> Option<(Vec, usize)> { + if self.spine.is_empty() { + return None; + } + + let offset = self.resolve_location(loc)?; + let (index, start_offset) = self.vertebra_coordinates(offset)?; + let page_index = self.page_index(offset, index, start_offset)?; + + self.cache.get(&index).map(|display_list| { + (display_list[page_index].iter().filter_map(|dc| { + match dc { + DrawCommand::Image(ImageCommand { rect, .. }) => Some((*rect).into()), + _ => None, + } + }).collect(), offset) + }) + } + fn pixmap(&mut self, loc: Location, scale: f32) -> Option<(Pixmap, usize)> { if self.spine.is_empty() { return None; diff --git a/src/document/html/mod.rs b/src/document/html/mod.rs index 17a10fac..07a2c3f5 100644 --- a/src/document/html/mod.rs +++ b/src/document/html/mod.rs @@ -15,7 +15,7 @@ use crate::framebuffer::Pixmap; use crate::helpers::{Normalize, decode_entities}; use crate::document::{Document, Location, TextLocation, TocEntry, BoundedText}; use crate::unit::pt_to_px; -use crate::geom::{Rectangle, Edge, CycleDir}; +use crate::geom::{Boundary, Edge, CycleDir}; use self::dom::{XmlTree, NodeRef}; use self::layout::{RootData, StyleData, DrawState, LoopContext}; use self::layout::{DrawCommand, TextCommand, ImageCommand, TextAlign}; @@ -159,18 +159,6 @@ impl HtmlDocument { } } - fn images(&mut self, loc: Location) -> Option<(Vec, usize)> { - let offset = self.resolve_location(loc)?; - let page_index = self.page_index(offset)?; - - Some((self.pages[page_index].iter().filter_map(|dc| { - match dc { - DrawCommand::Image(ImageCommand { rect, .. }) => Some(*rect), - _ => None, - } - }).collect(), offset)) - } - fn build_pages(&mut self) -> Vec { let mut stylesheet = StyleSheet::new(); let spine_dir = PathBuf::default(); @@ -353,6 +341,18 @@ impl Document for HtmlDocument { None } + fn images(&mut self, loc: Location) -> Option<(Vec, usize)> { + let offset = self.resolve_location(loc)?; + let page_index = self.page_index(offset)?; + + Some((self.pages[page_index].iter().filter_map(|dc| { + match dc { + DrawCommand::Image(ImageCommand { rect, .. }) => Some((*rect).into()), + _ => None, + } + }).collect(), offset)) + } + fn links(&mut self, loc: Location) -> Option<(Vec, usize)> { let offset = self.resolve_location(loc)?; let page_index = self.page_index(offset)?; diff --git a/src/document/mod.rs b/src/document/mod.rs index b2305cb1..0ff569e8 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -100,6 +100,7 @@ pub trait Document: Send+Sync { fn words(&mut self, loc: Location) -> Option<(Vec, usize)>; fn lines(&mut self, loc: Location) -> Option<(Vec, usize)>; fn links(&mut self, loc: Location) -> Option<(Vec, usize)>; + fn images(&mut self, loc: Location) -> Option<(Vec, usize)>; fn pixmap(&mut self, loc: Location, scale: f32) -> Option<(Pixmap, usize)>; fn layout(&mut self, width: u32, height: u32, font_size: f32, dpi: u16); diff --git a/src/document/mupdf_sys.rs b/src/document/mupdf_sys.rs index e8cb62f1..d938e074 100644 --- a/src/document/mupdf_sys.rs +++ b/src/document/mupdf_sys.rs @@ -126,7 +126,7 @@ pub struct FzStorable { #[repr(C)] pub struct FzTextOptions { - flags: libc::c_int, + pub flags: libc::c_int, } #[repr(C)] diff --git a/src/document/pdf.rs b/src/document/pdf.rs index b4e27998..00f6729b 100644 --- a/src/document/pdf.rs +++ b/src/document/pdf.rs @@ -227,6 +227,11 @@ impl Document for PdfDocument { self.page(index).and_then(|page| page.lines()).map(|lines| (lines, index)) } + fn images(&mut self, loc: Location) -> Option<(Vec, usize)> { + let index = self.resolve_location(loc)?; + self.page(index).and_then(|page| page.images()).map(|images| (images, index)) + } + fn links(&mut self, loc: Location) -> Option<(Vec, usize)> { let index = self.resolve_location(loc)?; self.page(index).and_then(|page| page.links()).map(|links| (links, index)) @@ -274,6 +279,32 @@ impl Document for PdfDocument { } impl<'a> PdfPage<'a> { + pub fn images(&self) -> Option> { + unsafe { + let mut images: Vec = Vec::new(); + let opts = FzTextOptions { flags: FZ_TEXT_PRESERVE_IMAGES }; + let tp = mp_new_stext_page_from_page(self.ctx.0, self.page, &opts); + if tp.is_null() { + return None; + } + + let mut block = (*tp).first_block; + + while !block.is_null() { + if (*block).kind == FZ_PAGE_BLOCK_IMAGE { + let bnd: Boundary = (*block).bbox.into(); + images.retain(|img| !img.overlaps(&bnd)); + images.push(bnd); + } + + block = (*block).next; + } + + fz_drop_stext_page(self.ctx.0, tp); + Some(images) + } + } + pub fn lines(&self) -> Option> { unsafe { let mut lines = Vec::new(); diff --git a/src/view/home/book.rs b/src/view/home/book.rs index 54d16ab1..6e5e3865 100644 --- a/src/view/home/book.rs +++ b/src/view/home/book.rs @@ -137,6 +137,10 @@ impl View for Book { let pt = pt!(self.rect.min.x + padding + dx, self.rect.min.y + x_height / 2 + dy); fb.draw_pixmap(&pixmap, pt); + if fb.inverted() { + let rect = pixmap.rect() + pt; + fb.invert_region(&rect); + } } } diff --git a/src/view/intermission.rs b/src/view/intermission.rs index a61b7f14..db52ede2 100644 --- a/src/view/intermission.rs +++ b/src/view/intermission.rs @@ -123,6 +123,10 @@ impl View for Intermission { let dy = (self.rect.height() as i32 - pixmap.height as i32) / 2; let pt = self.rect.min + pt!(dx, dy); fb.draw_pixmap(&pixmap, pt); + if fb.inverted() { + let rect = pixmap.rect() + pt; + fb.invert_region(&rect); + } } } }, diff --git a/src/view/reader/mod.rs b/src/view/reader/mod.rs index 121859d6..e371d291 100644 --- a/src/view/reader/mod.rs +++ b/src/view/reader/mod.rs @@ -70,6 +70,7 @@ pub struct Reader { chunks: Vec, // Chunks of pages being rendered. text: FxHashMap>, // Text of the current chunks. annotations: FxHashMap>, // Annotations for the current chunks. + noninverted_regions: FxHashMap>, focus: Option, search: Option, search_direction: LinearDir, @@ -337,9 +338,10 @@ impl Reader { children: Vec::new(), doc: Arc::new(Mutex::new(doc)), cache: BTreeMap::new(), + chunks: Vec::new(), text: FxHashMap::default(), annotations: FxHashMap::default(), - chunks: Vec::new(), + noninverted_regions: FxHashMap::default(), focus: None, search: None, search_direction: LinearDir::Forward, @@ -401,9 +403,10 @@ impl Reader { children: Vec::new(), doc: Arc::new(Mutex::new(Box::new(doc))), cache: BTreeMap::new(), + chunks: Vec::new(), text: FxHashMap::default(), annotations: FxHashMap::default(), - chunks: Vec::new(), + noninverted_regions: FxHashMap::default(), focus: None, search: None, search_direction: LinearDir::Forward, @@ -913,6 +916,18 @@ impl Reader { } } + #[inline] + fn update_noninverted_regions(&mut self, inverted: bool) { + self.noninverted_regions.clear(); + if inverted { + for chunk in &self.chunks { + if let Some((images, _)) = self.doc.lock().unwrap().images(Location::Exact(chunk.location)) { + self.noninverted_regions.insert(chunk.location, images); + } + } + } + } + #[inline] fn update_annotations(&mut self) { self.annotations.clear(); @@ -1033,6 +1048,7 @@ impl Reader { } self.update_annotations(); + self.update_noninverted_regions(context.fb.inverted()); if self.view_port.zoom_mode == ZoomMode::FitToPage || self.view_port.zoom_mode == ZoomMode::FitToWidth { @@ -3668,6 +3684,10 @@ impl View for Reader { } true }, + Event::Select(EntryId::ToggleInverted) => { + self.update_noninverted_regions(!context.fb.inverted()); + false + }, Event::Reseed => { self.reseed(rq, context); true @@ -3725,6 +3745,15 @@ impl View for Reader { let chunk_position = region_rect.min; fb.draw_framed_pixmap_contrast(pixmap, &chunk_frame, chunk_position, self.contrast.exponent, self.contrast.gray); + if let Some(rects) = self.noninverted_regions.get(&chunk.location) { + for r in rects { + let rect = (*r * scale).to_rect() - chunk.frame.min + chunk.position; + if let Some(ref image_rect) = rect.intersection(®ion_rect) { + fb.invert_region(image_rect); + } + } + } + if let Some(groups) = self.search.as_ref().and_then(|s| s.highlights.get(&chunk.location)) { for rects in groups { let mut last_rect: Option = None;