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:
jake 2023-05-20 23:08:04 +01:00 committed by GitHub
parent df70781af3
commit d8a47ad178
6 changed files with 19 additions and 38 deletions

View File

@ -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
View File

@ -6900,7 +6900,6 @@ version = "0.1.0"
dependencies = [
"image",
"libheif-rs",
"png",
"thiserror",
]

View File

@ -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",

View File

@ -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

View File

@ -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"

View File

@ -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)
}