image: Move background color guess to other thread

Also, skip it completely for JPEG and BMP

Issue #111
This commit is contained in:
Sophie Herold 2023-04-03 17:39:02 +02:00 • committed by Christopher Davis
parent 114c292225
commit 1b18486826
2 changed files with 71 additions and 43 deletions

View file

@ -810,8 +810,14 @@ impl LpImage {
new_tiles.cleanup(imp.zoom_target.get(), self.viewport());
new_tiles
});
if self.imp().background_color.borrow().is_none() {
self.set_background_color(self.background_color_guess());
if imp.background_color.borrow().is_none() {
spawn!(glib::clone!(@weak self as obj => async move {
let color = obj.background_color_guess().await;
obj.set_background_color(color);
if obj.is_mapped() {
obj.queue_draw();
}
}));
}
}
DecoderUpdate::Error(err) => {
@ -1635,7 +1641,16 @@ impl LpImage {
/// Returns a background color that should give suitable contrast with transparent images
///
/// For non-transparent images this always returns `BACKGROUND_COLOR_DEFAULT`
pub fn background_color_guess(&self) -> Option<gdk::RGBA> {
pub async fn background_color_guess(&self) -> Option<gdk::RGBA> {
// Shortcut for formats that don't support transparency
if !self
.format()
.map_or(true, |x| x.is_potentially_transparent())
{
log::debug!("This format does not support transparency");
return Some(*BACKGROUND_COLOR_DEFAULT);
}
let (width, height) = self.original_dimensions();
let max_size = i32::max(width, height);
@ -1660,58 +1675,64 @@ impl LpImage {
return None;
}
// Render the small version of the image and download to RAM
let texture = renderer.render_texture(node, None);
let mut downloader = gdk::TextureDownloader::new(&texture);
downloader.set_format(gdk::MemoryFormat::R8g8b8a8);
let (bytes, stride) = downloader.download_bytes();
let mut bytes_iter = bytes.iter();
let mut transparent = 0;
let mut bad_contrast = 0;
'img: loop {
for _ in 0..texture.width() {
let Some(r) = bytes_iter.next() else { break 'img; };
let Some(g) = bytes_iter.next() else { break 'img; };
let Some(b) = bytes_iter.next() else { break 'img; };
let Some(a) = bytes_iter.next() else { break 'img; };
gio::spawn_blocking(move || {
let mut bytes_iter = bytes.iter();
// Number of transparent pixels
let mut transparent = 0;
// Number of non-transparent pixels with bad contrast
let mut bad_contrast = 0;
'img: loop {
for _ in 0..texture.width() {
let Some(r) = bytes_iter.next() else { break 'img; };
let Some(g) = bytes_iter.next() else { break 'img; };
let Some(b) = bytes_iter.next() else { break 'img; };
let Some(a) = bytes_iter.next() else { break 'img; };
// 70% transparency
if *a < BACKGROUND_GUESS_TRANSPRAENT_PIXEL_THRESHOLD {
transparent += 1;
} else {
let fg = gdk::RGBA::new(
*r as f32 / 255.,
*g as f32 / 255.,
*b as f32 / 255.,
*a as f32 / 255.,
);
let contrast = crate::util::contrast_ratio(&BACKGROUND_COLOR_DEFAULT, &fg);
// 70% transparency
if *a < BACKGROUND_GUESS_TRANSPRAENT_PIXEL_THRESHOLD {
transparent += 1;
} else {
let fg = gdk::RGBA::new(
*r as f32 / 255.,
*g as f32 / 255.,
*b as f32 / 255.,
*a as f32 / 255.,
);
let contrast = crate::util::contrast_ratio(&BACKGROUND_COLOR_DEFAULT, &fg);
if contrast < BACKGROUND_GUESS_LOW_CONTRAST_RATIO {
bad_contrast += 1;
if contrast < BACKGROUND_GUESS_LOW_CONTRAST_RATIO {
bad_contrast += 1;
}
}
}
let advance_by = stride - 4 * texture.width() as usize;
if advance_by > 0 {
bytes_iter.nth(advance_by - 1);
}
}
let advance_by = stride - 4 * texture.width() as usize;
let n_pixels = texture.width() * texture.height();
if advance_by > 0 {
bytes_iter.nth(advance_by - 1);
let part_transparent = transparent as f64 / n_pixels as f64;
let part_bad_contrast = bad_contrast as f64 / (n_pixels as f64 - transparent as f64);
if part_transparent > BACKGROUND_GUESS_TRANSPRAENT_IMAGE_THRESHOLD
&& part_bad_contrast > BACKGROUND_GUESS_LOW_CONTRAST_TRHESHOLD
{
Some(*BACKGROUND_COLOR_ALTERNATE)
} else {
Some(*BACKGROUND_COLOR_DEFAULT)
}
}
let n_pixels = texture.width() * texture.height();
let part_transparent = transparent as f64 / n_pixels as f64;
let part_bad_contrast = bad_contrast as f64 / (n_pixels as f64 - transparent as f64);
if part_transparent > BACKGROUND_GUESS_TRANSPRAENT_IMAGE_THRESHOLD
&& part_bad_contrast > BACKGROUND_GUESS_LOW_CONTRAST_TRHESHOLD
{
Some(*BACKGROUND_COLOR_ALTERNATE)
} else {
Some(*BACKGROUND_COLOR_DEFAULT)
}
})
.await
.ok()?
}
}