mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
HEIF Fixes and Improvements (#838)
* limit HEIF to macos-only because of ubuntu's *archaic* `libheif` version * drop png entirely, go from raw `[r, g, b...]` -> `DynamicImage`
This commit is contained in:
parent
df70781af3
commit
d8a47ad178
13
.github/scripts/setup-system.sh
vendored
13
.github/scripts/setup-system.sh
vendored
@ -146,8 +146,6 @@ if [ "$SYSNAME" = "Linux" ]; then
|
||||
# FFmpeg dependencies
|
||||
DEBIAN_FFMPEG_DEPS="libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev ffmpeg"
|
||||
|
||||
DEBIAN_LIBHEIF_DEPS="libheif1 libheif-dev"
|
||||
|
||||
# Webkit2gtk requires gstreamer plugins for video playback to work
|
||||
DEBIAN_VIDEO_DEPS="gstreamer1.0-libav gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly"
|
||||
|
||||
@ -158,7 +156,7 @@ if [ "$SYSNAME" = "Linux" ]; then
|
||||
DEBIAN_LIBP2P_DEPS="protobuf-compiler"
|
||||
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install ${SPACEDRIVE_CUSTOM_APT_FLAGS:-} $DEBIAN_TAURI_DEPS $DEBIAN_FFMPEG_DEPS $DEBIAN_LIBHEIF_DEPS $DEBIAN_BINDGEN_DEPS $DEBIAN_LIBP2P_DEPS $DEBIAN_VIDEO_DEPS
|
||||
sudo apt-get -y install ${SPACEDRIVE_CUSTOM_APT_FLAGS:-} $DEBIAN_TAURI_DEPS $DEBIAN_FFMPEG_DEPS $DEBIAN_BINDGEN_DEPS $DEBIAN_LIBP2P_DEPS $DEBIAN_VIDEO_DEPS
|
||||
elif has pacman; then
|
||||
echo "Detected pacman!"
|
||||
echo "Installing dependencies with pacman..."
|
||||
@ -172,15 +170,13 @@ if [ "$SYSNAME" = "Linux" ]; then
|
||||
# FFmpeg dependencies
|
||||
ARCH_FFMPEG_DEPS="ffmpeg"
|
||||
|
||||
ARCH_LIBHEIF_DEPS="libheif"
|
||||
|
||||
# Bindgen dependencies - it's used by a dependency of Spacedrive
|
||||
ARCH_BINDGEN_DEPS="clang"
|
||||
|
||||
# Protobuf compiler - https://github.com/archlinux/svntogit-packages/blob/packages/protobuf/trunk/PKGBUILD provides `libprotoc`
|
||||
ARCH_LIBP2P_DEPS="protobuf"
|
||||
|
||||
sudo pacman -Sy --needed $ARCH_TAURI_DEPS $ARCH_FFMPEG_DEPS $ARCH_LIBHEIF_DEPS $ARCH_BINDGEN_DEPS $ARCH_LIBP2P_DEPS $ARCH_VIDEO_DEPS
|
||||
sudo pacman -Sy --needed $ARCH_TAURI_DEPS $ARCH_FFMPEG_DEPS $ARCH_BINDGEN_DEPS $ARCH_LIBP2P_DEPS $ARCH_VIDEO_DEPS
|
||||
elif has dnf; then
|
||||
echo "Detected dnf!"
|
||||
echo "Installing dependencies with dnf..."
|
||||
@ -202,9 +198,6 @@ if [ "$SYSNAME" = "Linux" ]; then
|
||||
# FFmpeg dependencies
|
||||
FEDORA_FFMPEG_DEPS="ffmpeg ffmpeg-devel"
|
||||
|
||||
# libheif dependencies
|
||||
FEDORA_LIBHEIF_DEPS="libheif libheif-devel"
|
||||
|
||||
# Webkit2gtk requires gstreamer plugins for video playback to work
|
||||
FEDORA_VIDEO_DEPS="gstreamer1-plugin-libav gstreamer1-plugins-base gstreamer1-plugins-good gstreamer1-plugins-good-extras gstreamer1-plugins-bad-free gstreamer1-plugins-bad-free-extras gstreamer1-plugins-ugly-free"
|
||||
|
||||
@ -226,7 +219,7 @@ if [ "$SYSNAME" = "Linux" ]; then
|
||||
'https://docs.fedoraproject.org/en-US/quick-docs/setup_rpmfusion'
|
||||
fi
|
||||
|
||||
sudo dnf install $FEDORA_TAURI_DEPS $FEDORA_BINDGEN_DEPS $FEDORA_LIBP2P_DEPS $FEDORA_VIDEO_DEPS $FEDORA_LIBHEIF_DEPS
|
||||
sudo dnf install $FEDORA_TAURI_DEPS $FEDORA_BINDGEN_DEPS $FEDORA_LIBP2P_DEPS $FEDORA_VIDEO_DEPS
|
||||
sudo dnf group install "C Development Tools and Libraries"
|
||||
else
|
||||
err "Your Linux distro '$(lsb_release -s -d)' is not supported by this script." \
|
||||
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6900,7 +6900,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"libheif-rs",
|
||||
"png",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
||||
@ -9,11 +9,7 @@ repository.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "1.3.0", features = [
|
||||
"api-all",
|
||||
"linux-protocol-headers",
|
||||
"macos-private-api",
|
||||
] }
|
||||
tauri = { version = "1.3.0", features = ["api-all", "linux-protocol-headers", "macos-private-api"] }
|
||||
rspc = { workspace = true, features = ["tauri"] }
|
||||
httpz = { workspace = true, features = [
|
||||
"axum",
|
||||
|
||||
@ -113,7 +113,7 @@ pub struct ThumbnailerJobStep {
|
||||
}
|
||||
|
||||
// TOOD(brxken128): validate avci and avcs
|
||||
#[cfg(all(feature = "heif", any(target_os = "macos", target_os = "linux")))]
|
||||
#[cfg(all(feature = "heif", target_os = "macos"))]
|
||||
const HEIF_EXTENSIONS: [&str; 7] = ["heif", "heifs", "heic", "heics", "avif", "avci", "avcs"];
|
||||
|
||||
pub async fn generate_image_thumbnail<P: AsRef<Path>>(
|
||||
@ -122,7 +122,7 @@ pub async fn generate_image_thumbnail<P: AsRef<Path>>(
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Webp creation has blocking code
|
||||
let webp = block_in_place(|| -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
#[cfg(all(feature = "heif", any(target_os = "macos", target_os = "linux")))]
|
||||
#[cfg(all(feature = "heif", target_os = "macos"))]
|
||||
let img = {
|
||||
let ext = file_path.as_ref().extension().unwrap().to_ascii_lowercase();
|
||||
if HEIF_EXTENSIONS
|
||||
@ -135,7 +135,7 @@ pub async fn generate_image_thumbnail<P: AsRef<Path>>(
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(all(feature = "heif", any(target_os = "macos", target_os = "linux"))))]
|
||||
#[cfg(not(all(feature = "heif", target_os = "macos")))]
|
||||
let img = image::open(file_path)?;
|
||||
|
||||
let (w, h) = img.dimensions();
|
||||
@ -183,13 +183,13 @@ pub const fn can_generate_thumbnail_for_video(video_extension: &VideoExtension)
|
||||
pub const fn can_generate_thumbnail_for_image(image_extension: &ImageExtension) -> bool {
|
||||
use ImageExtension::*;
|
||||
|
||||
#[cfg(all(feature = "heif", any(target_os = "macos", target_os = "linux")))]
|
||||
#[cfg(all(feature = "heif", target_os = "macos"))]
|
||||
let res = matches!(
|
||||
image_extension,
|
||||
Jpg | Jpeg | Png | Webp | Gif | Heic | Heics | Heif | Heifs | Avif
|
||||
);
|
||||
|
||||
#[cfg(not(all(feature = "heif", any(target_os = "macos", target_os = "linux"))))]
|
||||
#[cfg(not(all(feature = "heif", target_os = "macos")))]
|
||||
let res = matches!(image_extension, Jpg | Jpeg | Png | Webp | Gif);
|
||||
|
||||
res
|
||||
|
||||
@ -8,6 +8,5 @@ edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
libheif-rs = "0.19.2"
|
||||
png = "0.17.8"
|
||||
thiserror = "1.0.40"
|
||||
image = "0.24.6"
|
||||
thiserror = "1.0.40"
|
||||
|
||||
@ -6,7 +6,6 @@ use std::{
|
||||
|
||||
use image::DynamicImage;
|
||||
use libheif_rs::{ColorSpace, HeifContext, LibHeif, RgbChroma};
|
||||
use png::{BitDepth, ColorType};
|
||||
use thiserror::Error;
|
||||
|
||||
type HeifResult<T> = Result<T, HeifError>;
|
||||
@ -20,12 +19,12 @@ const HEIF_MAXIMUM_FILE_SIZE: u64 = 1048576 * 20;
|
||||
pub enum HeifError {
|
||||
#[error("error with libheif: {0}")]
|
||||
LibHeif(#[from] libheif_rs::HeifError),
|
||||
#[error("error while encoding to png: {0}")]
|
||||
PngEncode(#[from] png::EncodingError),
|
||||
#[error("error while loading the image (via the `image` crate): {0}")]
|
||||
Image(#[from] image::ImageError),
|
||||
#[error("io error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("there was an error while converting the image to an `RgbImage`")]
|
||||
RgbImageConversion,
|
||||
#[error("the image provided is unsupported")]
|
||||
Unsupported,
|
||||
#[error("the image provided is too large (over 20MiB)")]
|
||||
@ -53,6 +52,10 @@ pub fn heif_to_dynamic_image(path: &Path) -> HeifResult<DynamicImage> {
|
||||
// TODO(brxken128): add support for images with individual r/g/b channels
|
||||
// i'm unable to find a sample to test with, but it should follow the same principles as this one
|
||||
if let Some(i) = img.planes().interleaved {
|
||||
if i.bits_per_pixel != 8 {
|
||||
return Err(HeifError::InvalidBitDepth);
|
||||
}
|
||||
|
||||
let data = i.data.to_vec();
|
||||
let mut reader = Cursor::new(data);
|
||||
|
||||
@ -70,19 +73,10 @@ pub fn heif_to_dynamic_image(path: &Path) -> HeifResult<DynamicImage> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut writer = Cursor::new(vec![]);
|
||||
let rgb_img = image::RgbImage::from_raw(img.width(), img.height(), sequence)
|
||||
.ok_or(HeifError::RgbImageConversion)?;
|
||||
|
||||
let mut png_encoder = png::Encoder::new(&mut writer, i.width, i.height);
|
||||
png_encoder.set_color(ColorType::Rgb);
|
||||
png_encoder
|
||||
.set_depth(BitDepth::from_u8(i.bits_per_pixel).ok_or(HeifError::InvalidBitDepth)?);
|
||||
|
||||
let mut png_writer = png_encoder.write_header()?;
|
||||
png_writer.write_image_data(&sequence)?;
|
||||
png_writer.finish()?;
|
||||
|
||||
image::load_from_memory_with_format(&writer.into_inner(), image::ImageFormat::Png)
|
||||
.map_err(HeifError::Image)
|
||||
Ok(DynamicImage::ImageRgb8(rgb_img))
|
||||
} else {
|
||||
Err(HeifError::Unsupported)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user