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),
|
||||
zoom_level: tiling::zoom_to_level(1.),
|
||||
bleed: 0,
|
||||
texture: decoded.into_texture(),
|
||||
texture: decoded.into_texture()?,
|
||||
};
|
||||
|
||||
tiles.push(tile);
|
||||
|
|
@ -109,18 +109,21 @@ pub struct 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 {
|
||||
RgbChroma::HdrRgbBe
|
||||
| RgbChroma::HdrRgbaBe
|
||||
| RgbChroma::HdrRgbLe
|
||||
| RgbChroma::HdrRgbaLe => {
|
||||
let transmuted: &mut [u16] =
|
||||
safe_transmute::transmute_many_pedantic_mut(self.plane.data).unwrap();
|
||||
|
||||
// Scale HDR pixels to 16bit (they are usually 10bit or 12bit)
|
||||
for pixel in transmuted.iter_mut() {
|
||||
*pixel <<= 16 - self.plane.bits_per_pixel;
|
||||
if let Ok(transmuted) =
|
||||
safe_transmute::transmute_many_pedantic_mut::<u16>(self.plane.data)
|
||||
{
|
||||
// Scale HDR pixels to 16bit (they are usually 10bit or 12bit)
|
||||
for pixel in transmuted.iter_mut() {
|
||||
*pixel <<= 16 - self.plane.bits_per_pixel;
|
||||
}
|
||||
} else {
|
||||
log::error!("Could not transform HDR (16bit) data to u16");
|
||||
}
|
||||
|
||||
if self.has_alpha_channel {
|
||||
|
|
@ -157,6 +160,6 @@ impl<'a> Decoded<'a> {
|
|||
self.plane.stride,
|
||||
);
|
||||
|
||||
tex.upcast()
|
||||
Ok(tex.upcast())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::deps::*;
|
|||
use crate::util::gettext::*;
|
||||
use crate::util::{BufReadSeek, ToBufRead};
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{bail, Context};
|
||||
use arc_swap::ArcSwap;
|
||||
use gtk::prelude::*;
|
||||
use image_rs::AnimationDecoder;
|
||||
|
|
@ -21,8 +21,12 @@ pub struct ImageRsOther {
|
|||
impl Drop for ImageRsOther {
|
||||
fn drop(&mut self) {
|
||||
if let Some(handle) = &self.thread_handle {
|
||||
let mut abort = self.abort.write().unwrap();
|
||||
*abort = true;
|
||||
if let Ok(mut abort) = self.abort.write() {
|
||||
*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();
|
||||
}
|
||||
}
|
||||
|
|
@ -95,7 +99,7 @@ impl ImageRsOther {
|
|||
position: (0, 0),
|
||||
zoom_level: tiling::zoom_to_level(1.),
|
||||
bleed: 0,
|
||||
texture: decoded_image.into_texture(),
|
||||
texture: decoded_image.into_texture()?,
|
||||
};
|
||||
|
||||
tiles.push(tile);
|
||||
|
|
@ -179,7 +183,7 @@ impl ImageRsOther {
|
|||
position,
|
||||
zoom_level: tiling::zoom_to_level(1.),
|
||||
bleed: 0,
|
||||
texture: decoded_image.into_texture(),
|
||||
texture: decoded_image.into_texture()?,
|
||||
};
|
||||
|
||||
tiles.push_frame(tile, dimensions, delay);
|
||||
|
|
@ -196,9 +200,13 @@ impl ImageRsOther {
|
|||
std::thread::park();
|
||||
}
|
||||
|
||||
if *abort.read().unwrap() {
|
||||
log::debug!("Terminating decoder thread.");
|
||||
return Ok(());
|
||||
if let Ok(abort_state) = abort.read().as_deref() {
|
||||
if *abort_state {
|
||||
log::debug!("Terminating decoder thread.");
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
bail!("Cannot read decoder thread abort instructions.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -219,7 +227,7 @@ pub struct 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 {
|
||||
img @ image_rs::DynamicImage::ImageLuma8(_) => {
|
||||
let buffer = img.to_rgb8();
|
||||
|
|
@ -299,20 +307,23 @@ impl Decoded {
|
|||
2,
|
||||
gdk::MemoryFormat::R16g16b16a16,
|
||||
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);
|
||||
|
||||
gdk::MemoryTexture::new(
|
||||
let texture = gdk::MemoryTexture::new(
|
||||
layout.width as i32,
|
||||
layout.height as i32,
|
||||
memory_format,
|
||||
&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 svg::{Svg, RSVG_MAX_SIZE};
|
||||
|
||||
use super::{Decoder, DecoderUpdate, UpdateSender};
|
||||
use super::{DecoderUpdate, UpdateSender};
|
||||
use crate::util::gettext::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
|||
|
|
@ -36,8 +36,12 @@ impl Request {
|
|||
|
||||
impl Drop for Svg {
|
||||
fn drop(&mut self) {
|
||||
let mut request = self.current_request.write().unwrap();
|
||||
*request = Request::Exit;
|
||||
if let Ok(mut request) = self.current_request.write() {
|
||||
*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();
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +69,7 @@ impl Svg {
|
|||
|
||||
loop {
|
||||
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();
|
||||
*request = Request::None;
|
||||
value
|
||||
|
|
@ -90,7 +94,12 @@ impl Svg {
|
|||
let relevant_tiles = tiling.relevant_tiles(&tile_request.viewport);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +148,7 @@ impl Svg {
|
|||
),
|
||||
zoom_level: tiling::zoom_to_level(tile_instructions.tiling.zoom),
|
||||
bleed: 2,
|
||||
texture: decoded_image.into_texture(),
|
||||
texture: decoded_image.into_texture()?,
|
||||
};
|
||||
|
||||
tiles.push(tile.clone());
|
||||
|
|
@ -157,8 +166,12 @@ impl Svg {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, request: TileRequest, _abstract_decoder: &Decoder) -> anyhow::Result<()> {
|
||||
let mut current_request = self.current_request.write().unwrap();
|
||||
pub fn request(&self, request: TileRequest) -> anyhow::Result<()> {
|
||||
let mut current_request = self
|
||||
.current_request
|
||||
.write()
|
||||
.ok()
|
||||
.context("RwLock is poisoned")?;
|
||||
*current_request = Request::Tile(request);
|
||||
self.thread_handle.thread().unpark();
|
||||
|
||||
|
|
@ -200,7 +213,7 @@ struct Decoded {
|
|||
}
|
||||
|
||||
impl Decoded {
|
||||
pub fn into_texture(self) -> gdk::Texture {
|
||||
pub fn into_texture(self) -> anyhow::Result<gdk::Texture> {
|
||||
let memory_format = {
|
||||
#[cfg(target_endian = "little")]
|
||||
{
|
||||
|
|
@ -217,8 +230,13 @@ impl Decoded {
|
|||
let height = self.surface.height();
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue