decoder: Avoid unwraps and report issues to UI
Those are mostly cases that should never occur. But especially for rare erros it can be helpful to directly display them in the UI.
This commit is contained in:
parent
5a93ec71da
commit
e8aa5451c3
5 changed files with 86 additions and 37 deletions
|
|
@ -89,7 +89,7 @@ impl Heif {
|
||||||
position: (0, 0),
|
position: (0, 0),
|
||||||
zoom_level: tiling::zoom_to_level(1.),
|
zoom_level: tiling::zoom_to_level(1.),
|
||||||
bleed: 0,
|
bleed: 0,
|
||||||
texture: decoded.into_texture(),
|
texture: decoded.into_texture()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
tiles.push(tile);
|
tiles.push(tile);
|
||||||
|
|
@ -109,18 +109,21 @@ pub struct Decoded<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Decoded<'a> {
|
impl<'a> Decoded<'a> {
|
||||||
pub fn into_texture(self) -> gdk::Texture {
|
pub fn into_texture(self) -> anyhow::Result<gdk::Texture> {
|
||||||
let memory_format = match self.rgb_chroma {
|
let memory_format = match self.rgb_chroma {
|
||||||
RgbChroma::HdrRgbBe
|
RgbChroma::HdrRgbBe
|
||||||
| RgbChroma::HdrRgbaBe
|
| RgbChroma::HdrRgbaBe
|
||||||
| RgbChroma::HdrRgbLe
|
| RgbChroma::HdrRgbLe
|
||||||
| RgbChroma::HdrRgbaLe => {
|
| RgbChroma::HdrRgbaLe => {
|
||||||
let transmuted: &mut [u16] =
|
if let Ok(transmuted) =
|
||||||
safe_transmute::transmute_many_pedantic_mut(self.plane.data).unwrap();
|
safe_transmute::transmute_many_pedantic_mut::<u16>(self.plane.data)
|
||||||
|
{
|
||||||
// Scale HDR pixels to 16bit (they are usually 10bit or 12bit)
|
// Scale HDR pixels to 16bit (they are usually 10bit or 12bit)
|
||||||
for pixel in transmuted.iter_mut() {
|
for pixel in transmuted.iter_mut() {
|
||||||
*pixel <<= 16 - self.plane.bits_per_pixel;
|
*pixel <<= 16 - self.plane.bits_per_pixel;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!("Could not transform HDR (16bit) data to u16");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.has_alpha_channel {
|
if self.has_alpha_channel {
|
||||||
|
|
@ -157,6 +160,6 @@ impl<'a> Decoded<'a> {
|
||||||
self.plane.stride,
|
self.plane.stride,
|
||||||
);
|
);
|
||||||
|
|
||||||
tex.upcast()
|
Ok(tex.upcast())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::deps::*;
|
||||||
use crate::util::gettext::*;
|
use crate::util::gettext::*;
|
||||||
use crate::util::{BufReadSeek, ToBufRead};
|
use crate::util::{BufReadSeek, ToBufRead};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::{bail, Context};
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use image_rs::AnimationDecoder;
|
use image_rs::AnimationDecoder;
|
||||||
|
|
@ -21,8 +21,12 @@ pub struct ImageRsOther {
|
||||||
impl Drop for ImageRsOther {
|
impl Drop for ImageRsOther {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(handle) = &self.thread_handle {
|
if let Some(handle) = &self.thread_handle {
|
||||||
let mut abort = self.abort.write().unwrap();
|
if let Ok(mut abort) = self.abort.write() {
|
||||||
*abort = true;
|
*abort = true;
|
||||||
|
} else {
|
||||||
|
// Don't use log in drop because not sure if always still initialized
|
||||||
|
eprintln!("Unable to write abort signal to decoding thread.");
|
||||||
|
}
|
||||||
handle.thread().unpark();
|
handle.thread().unpark();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +99,7 @@ impl ImageRsOther {
|
||||||
position: (0, 0),
|
position: (0, 0),
|
||||||
zoom_level: tiling::zoom_to_level(1.),
|
zoom_level: tiling::zoom_to_level(1.),
|
||||||
bleed: 0,
|
bleed: 0,
|
||||||
texture: decoded_image.into_texture(),
|
texture: decoded_image.into_texture()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
tiles.push(tile);
|
tiles.push(tile);
|
||||||
|
|
@ -179,7 +183,7 @@ impl ImageRsOther {
|
||||||
position,
|
position,
|
||||||
zoom_level: tiling::zoom_to_level(1.),
|
zoom_level: tiling::zoom_to_level(1.),
|
||||||
bleed: 0,
|
bleed: 0,
|
||||||
texture: decoded_image.into_texture(),
|
texture: decoded_image.into_texture()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
tiles.push_frame(tile, dimensions, delay);
|
tiles.push_frame(tile, dimensions, delay);
|
||||||
|
|
@ -196,9 +200,13 @@ impl ImageRsOther {
|
||||||
std::thread::park();
|
std::thread::park();
|
||||||
}
|
}
|
||||||
|
|
||||||
if *abort.read().unwrap() {
|
if let Ok(abort_state) = abort.read().as_deref() {
|
||||||
log::debug!("Terminating decoder thread.");
|
if *abort_state {
|
||||||
return Ok(());
|
log::debug!("Terminating decoder thread.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bail!("Cannot read decoder thread abort instructions.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +227,7 @@ pub struct Decoded {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoded {
|
impl Decoded {
|
||||||
pub fn into_texture(self) -> gdk::Texture {
|
pub fn into_texture(self) -> anyhow::Result<gdk::Texture> {
|
||||||
let (n_bytes, memory_format, layout, data) = match self.dynamic_image {
|
let (n_bytes, memory_format, layout, data) = match self.dynamic_image {
|
||||||
img @ image_rs::DynamicImage::ImageLuma8(_) => {
|
img @ image_rs::DynamicImage::ImageLuma8(_) => {
|
||||||
let buffer = img.to_rgb8();
|
let buffer = img.to_rgb8();
|
||||||
|
|
@ -299,20 +307,23 @@ impl Decoded {
|
||||||
2,
|
2,
|
||||||
gdk::MemoryFormat::R16g16b16a16,
|
gdk::MemoryFormat::R16g16b16a16,
|
||||||
buffer.sample_layout(),
|
buffer.sample_layout(),
|
||||||
safe_transmute::transmute_vec(buffer.into_raw()).unwrap(),
|
safe_transmute::transmute_vec(buffer.into_raw())
|
||||||
|
.context("Failed to transform into 16 bit texture")?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let bytes = glib::Bytes::from_owned(data);
|
let bytes = glib::Bytes::from_owned(data);
|
||||||
|
|
||||||
gdk::MemoryTexture::new(
|
let texture = gdk::MemoryTexture::new(
|
||||||
layout.width as i32,
|
layout.width as i32,
|
||||||
layout.height as i32,
|
layout.height as i32,
|
||||||
memory_format,
|
memory_format,
|
||||||
&bytes,
|
&bytes,
|
||||||
layout.height_stride * n_bytes,
|
layout.height_stride * n_bytes,
|
||||||
)
|
)
|
||||||
.upcast()
|
.upcast();
|
||||||
|
|
||||||
|
Ok(texture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub use heif::Heif;
|
||||||
pub use image_rs_other::ImageRsOther;
|
pub use image_rs_other::ImageRsOther;
|
||||||
pub use svg::{Svg, RSVG_MAX_SIZE};
|
pub use svg::{Svg, RSVG_MAX_SIZE};
|
||||||
|
|
||||||
use super::{Decoder, DecoderUpdate, UpdateSender};
|
use super::{DecoderUpdate, UpdateSender};
|
||||||
use crate::util::gettext::*;
|
use crate::util::gettext::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,12 @@ impl Request {
|
||||||
|
|
||||||
impl Drop for Svg {
|
impl Drop for Svg {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut request = self.current_request.write().unwrap();
|
if let Ok(mut request) = self.current_request.write() {
|
||||||
*request = Request::Exit;
|
*request = Request::Exit;
|
||||||
|
} else {
|
||||||
|
// Don't use log in drop because not sure if always still initialized
|
||||||
|
eprintln!("Unable to write exit request: RwLock is poisoned.");
|
||||||
|
}
|
||||||
self.thread_handle.thread().unpark();
|
self.thread_handle.thread().unpark();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +69,7 @@ impl Svg {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let tile_request = {
|
let tile_request = {
|
||||||
let mut request = request_store.write().unwrap();
|
let mut request = request_store.write().ok().context("RwLock is poisoned")?;
|
||||||
let value = request.clone();
|
let value = request.clone();
|
||||||
*request = Request::None;
|
*request = Request::None;
|
||||||
value
|
value
|
||||||
|
|
@ -90,7 +94,12 @@ impl Svg {
|
||||||
let relevant_tiles = tiling.relevant_tiles(&tile_request.viewport);
|
let relevant_tiles = tiling.relevant_tiles(&tile_request.viewport);
|
||||||
|
|
||||||
for tile_instructions in relevant_tiles {
|
for tile_instructions in relevant_tiles {
|
||||||
if request_store.read().unwrap().is_waiting() {
|
if request_store
|
||||||
|
.read()
|
||||||
|
.ok()
|
||||||
|
.context("RwLock is poisoned")?
|
||||||
|
.is_waiting()
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +148,7 @@ impl Svg {
|
||||||
),
|
),
|
||||||
zoom_level: tiling::zoom_to_level(tile_instructions.tiling.zoom),
|
zoom_level: tiling::zoom_to_level(tile_instructions.tiling.zoom),
|
||||||
bleed: 2,
|
bleed: 2,
|
||||||
texture: decoded_image.into_texture(),
|
texture: decoded_image.into_texture()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
tiles.push(tile.clone());
|
tiles.push(tile.clone());
|
||||||
|
|
@ -157,8 +166,12 @@ impl Svg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request(&self, request: TileRequest, _abstract_decoder: &Decoder) -> anyhow::Result<()> {
|
pub fn request(&self, request: TileRequest) -> anyhow::Result<()> {
|
||||||
let mut current_request = self.current_request.write().unwrap();
|
let mut current_request = self
|
||||||
|
.current_request
|
||||||
|
.write()
|
||||||
|
.ok()
|
||||||
|
.context("RwLock is poisoned")?;
|
||||||
*current_request = Request::Tile(request);
|
*current_request = Request::Tile(request);
|
||||||
self.thread_handle.thread().unpark();
|
self.thread_handle.thread().unpark();
|
||||||
|
|
||||||
|
|
@ -200,7 +213,7 @@ struct Decoded {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoded {
|
impl Decoded {
|
||||||
pub fn into_texture(self) -> gdk::Texture {
|
pub fn into_texture(self) -> anyhow::Result<gdk::Texture> {
|
||||||
let memory_format = {
|
let memory_format = {
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
{
|
{
|
||||||
|
|
@ -217,8 +230,13 @@ impl Decoded {
|
||||||
let height = self.surface.height();
|
let height = self.surface.height();
|
||||||
let stride = self.surface.stride() as usize;
|
let stride = self.surface.stride() as usize;
|
||||||
|
|
||||||
let bytes = glib::Bytes::from_owned(self.surface.take_data().unwrap().to_vec());
|
let bytes = glib::Bytes::from_owned(
|
||||||
|
self.surface
|
||||||
|
.take_data()
|
||||||
|
.context("Cairo surface already taken")?
|
||||||
|
.to_vec(),
|
||||||
|
);
|
||||||
|
|
||||||
gdk::MemoryTexture::new(width, height, memory_format, &bytes, stride).upcast()
|
Ok(gdk::MemoryTexture::new(width, height, memory_format, &bytes, stride).upcast())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ impl UpdateSender {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Decoder {
|
pub struct Decoder {
|
||||||
decoder: FormatDecoder,
|
decoder: FormatDecoder,
|
||||||
|
update_sender: UpdateSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -100,11 +101,23 @@ impl Decoder {
|
||||||
let update_sender = UpdateSender { sender };
|
let update_sender = UpdateSender { sender };
|
||||||
tiles.set_update_sender(update_sender.clone());
|
tiles.set_update_sender(update_sender.clone());
|
||||||
|
|
||||||
let decoder = gio::spawn_blocking(move || Self::format_decoder(update_sender, file, tiles))
|
let decoder = gio::spawn_blocking(
|
||||||
.await
|
glib::clone!(@strong update_sender => move || Self::format_decoder(
|
||||||
.map_err(|_| anyhow!("Constructing the FormatDecoder failed unexpectedly"))??;
|
update_sender,
|
||||||
|
file,
|
||||||
|
tiles
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| anyhow!("Constructing the FormatDecoder failed unexpectedly"))??;
|
||||||
|
|
||||||
Ok((Self { decoder }, receiver))
|
Ok((
|
||||||
|
Self {
|
||||||
|
decoder,
|
||||||
|
update_sender,
|
||||||
|
},
|
||||||
|
receiver,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_decoder(
|
fn format_decoder(
|
||||||
|
|
@ -171,7 +184,11 @@ impl Decoder {
|
||||||
/// Request missing tiles
|
/// Request missing tiles
|
||||||
pub fn request(&self, tile_request: TileRequest) {
|
pub fn request(&self, tile_request: TileRequest) {
|
||||||
match &self.decoder {
|
match &self.decoder {
|
||||||
FormatDecoder::Svg(svg) => svg.request(tile_request, self).unwrap(),
|
FormatDecoder::Svg(svg) => {
|
||||||
|
if let Err(err) = svg.request(tile_request) {
|
||||||
|
self.update_sender.send(DecoderUpdate::Error(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
FormatDecoder::Heif(_) => {}
|
FormatDecoder::Heif(_) => {}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue