image: Move background color guess to other thread
Also, skip it completely for JPEG and BMP Issue #111
This commit is contained in:
parent
114c292225
commit
1b18486826
2 changed files with 71 additions and 43 deletions
|
|
@ -31,6 +31,13 @@ impl ImageFormat {
|
||||||
pub fn is_svg(&self) -> bool {
|
pub fn is_svg(&self) -> bool {
|
||||||
matches!(self, Self::Svg)
|
matches!(self, Self::Svg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_potentially_transparent(&self) -> bool {
|
||||||
|
!matches!(
|
||||||
|
self,
|
||||||
|
Self::ImageRs(image_rs::ImageFormat::Bmp) | Self::ImageRs(image_rs::ImageFormat::Jpeg)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ImageFormat {
|
impl std::fmt::Display for ImageFormat {
|
||||||
|
|
|
||||||
|
|
@ -810,8 +810,14 @@ impl LpImage {
|
||||||
new_tiles.cleanup(imp.zoom_target.get(), self.viewport());
|
new_tiles.cleanup(imp.zoom_target.get(), self.viewport());
|
||||||
new_tiles
|
new_tiles
|
||||||
});
|
});
|
||||||
if self.imp().background_color.borrow().is_none() {
|
if imp.background_color.borrow().is_none() {
|
||||||
self.set_background_color(self.background_color_guess());
|
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) => {
|
DecoderUpdate::Error(err) => {
|
||||||
|
|
@ -1635,7 +1641,16 @@ impl LpImage {
|
||||||
/// Returns a background color that should give suitable contrast with transparent images
|
/// Returns a background color that should give suitable contrast with transparent images
|
||||||
///
|
///
|
||||||
/// For non-transparent images this always returns `BACKGROUND_COLOR_DEFAULT`
|
/// 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 (width, height) = self.original_dimensions();
|
||||||
let max_size = i32::max(width, height);
|
let max_size = i32::max(width, height);
|
||||||
|
|
||||||
|
|
@ -1660,58 +1675,64 @@ impl LpImage {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render the small version of the image and download to RAM
|
||||||
let texture = renderer.render_texture(node, None);
|
let texture = renderer.render_texture(node, None);
|
||||||
|
|
||||||
let mut downloader = gdk::TextureDownloader::new(&texture);
|
let mut downloader = gdk::TextureDownloader::new(&texture);
|
||||||
downloader.set_format(gdk::MemoryFormat::R8g8b8a8);
|
downloader.set_format(gdk::MemoryFormat::R8g8b8a8);
|
||||||
let (bytes, stride) = downloader.download_bytes();
|
let (bytes, stride) = downloader.download_bytes();
|
||||||
|
|
||||||
let mut bytes_iter = bytes.iter();
|
gio::spawn_blocking(move || {
|
||||||
let mut transparent = 0;
|
let mut bytes_iter = bytes.iter();
|
||||||
let mut bad_contrast = 0;
|
// Number of transparent pixels
|
||||||
'img: loop {
|
let mut transparent = 0;
|
||||||
for _ in 0..texture.width() {
|
// Number of non-transparent pixels with bad contrast
|
||||||
let Some(r) = bytes_iter.next() else { break 'img; };
|
let mut bad_contrast = 0;
|
||||||
let Some(g) = bytes_iter.next() else { break 'img; };
|
'img: loop {
|
||||||
let Some(b) = bytes_iter.next() else { break 'img; };
|
for _ in 0..texture.width() {
|
||||||
let Some(a) = bytes_iter.next() else { break 'img; };
|
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
|
// 70% transparency
|
||||||
if *a < BACKGROUND_GUESS_TRANSPRAENT_PIXEL_THRESHOLD {
|
if *a < BACKGROUND_GUESS_TRANSPRAENT_PIXEL_THRESHOLD {
|
||||||
transparent += 1;
|
transparent += 1;
|
||||||
} else {
|
} else {
|
||||||
let fg = gdk::RGBA::new(
|
let fg = gdk::RGBA::new(
|
||||||
*r as f32 / 255.,
|
*r as f32 / 255.,
|
||||||
*g as f32 / 255.,
|
*g as f32 / 255.,
|
||||||
*b as f32 / 255.,
|
*b as f32 / 255.,
|
||||||
*a as f32 / 255.,
|
*a as f32 / 255.,
|
||||||
);
|
);
|
||||||
let contrast = crate::util::contrast_ratio(&BACKGROUND_COLOR_DEFAULT, &fg);
|
let contrast = crate::util::contrast_ratio(&BACKGROUND_COLOR_DEFAULT, &fg);
|
||||||
|
|
||||||
if contrast < BACKGROUND_GUESS_LOW_CONTRAST_RATIO {
|
if contrast < BACKGROUND_GUESS_LOW_CONTRAST_RATIO {
|
||||||
bad_contrast += 1;
|
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 {
|
let part_transparent = transparent as f64 / n_pixels as f64;
|
||||||
bytes_iter.nth(advance_by - 1);
|
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
|
||||||
let n_pixels = texture.width() * texture.height();
|
.ok()?
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue