Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ arboard = { version = "3.4", features = ["wayland-data-control"] }
clap = "3.2" # DO NOT UPDATE unless you test extensively on mac. This will break the mac app.
dirs = "6.0"
env_logger = "0.11"
yuv = { version = "0.8", optional = true }

[features]
default = [
Expand All @@ -132,7 +133,7 @@ default = [
"j2k",
"jxlcms",
]
heif = ["libheif-rs"]
heif = ["libheif-rs", "yuv"]
avif_native = ["avif-decode"]
dav1d = ["libavif-image"]
file_open = ["rfd"]
Expand Down
140 changes: 112 additions & 28 deletions src/image_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub fn open_image(
#[cfg(feature = "heif")]
"heif" | "heic" => {
// Built on work in https://github.com/rsuu/rmg - thanks!
use libheif_rs::{ColorSpace, HeifContext, LibHeif, RgbChroma};
use libheif_rs::{Chroma, ColorSpace, HeifContext, LibHeif, RgbChroma};

let lib_heif = LibHeif::new();
let mut ctx = HeifContext::read_from_file(&img_location.to_string_lossy().to_string())?;
Expand All @@ -173,34 +173,118 @@ pub fn open_image(
ctx.set_security_limits(&limits)?;
}
let handle = ctx.primary_image_handle()?;
let img = lib_heif.decode(&handle, ColorSpace::Rgb(RgbChroma::Rgba), None)?;
let planes = img.planes();
let interleaved = planes
.interleaved
.context("Can't create interleaved plane")?;

let data = interleaved.data;
let width = interleaved.width;
let height = interleaved.height;
let stride = interleaved.stride;

let mut res: Vec<u8> = Vec::new();
for y in 0..height {
let mut step = y as usize * stride;

for _ in 0..width {
res.extend_from_slice(&[
data[step],
data[step + 1],
data[step + 2],
data[step + 3],
]);
step += 4;
let img = lib_heif.decode(&handle, ColorSpace::Undefined, None)?;

let i = match img.color_space() {
Some(ColorSpace::Rgb(chroma @ (RgbChroma::Rgb | RgbChroma::Rgba))) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libheif can use 10 and 12 bits as well, not only 8 bit images.

let planes = img.planes();
let interleaved = planes
.interleaved
.context("Can't create interleaved plane")?;

let data = interleaved.data;
let width = interleaved.width;
let height = interleaved.height;

match chroma {
RgbChroma::Rgb => DynamicImage::ImageRgb8(
image::RgbImage::from_vec(width, height, data.to_vec())
.context("Failed creating RGB image from HEIC")?,
),
RgbChroma::Rgba => DynamicImage::ImageRgba8(
image::RgbaImage::from_vec(width, height, data.to_vec())
.context("Failed creating RGBA image from HEIC")?,
),
_ => unreachable!("Guarded by Rgb | Rgba"),
}
}
}
let buf = image::ImageBuffer::from_vec(width as u32, height as u32, res)
.context("Can't create HEIC/HEIF ImageBuffer with given res")?;
let i = DynamicImage::ImageRgba8(buf);
Some(ColorSpace::YCbCr(chroma)) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this branch is correct one.

It's missing:

  • Checking the image bit depth, and handling or rejecting unsupported depths. Typically, libheif supports 8-bit and 10-bit for HEIC.
  • Accounting that YUV range is subject to change.
  • Accounting for the YUV matrix, which may vary. For HEIC, it is usually BT.709 rather than BT.601. But it's needed to be queried from libheif.

let planes = img.planes();
let y = planes.y.context("YUV is missing Y")?;
let y_plane = y.data;
let y_stride = y.stride.try_into()?;
let u = planes.cb.context("YUV is missing U")?;
let u_plane = u.data;
let u_stride = u.stride.try_into()?;
let v = planes.cr.context("YUV is missing V")?;
let v_plane = v.data;
let v_stride = v.stride.try_into()?;
let width = img.width();
let height = img.height();
let planar_image = yuv::YuvPlanarImage {
y_plane,
y_stride,
u_plane,
u_stride,
v_plane,
v_stride,
width,
height,
};

let rgba_stride = width * 4;
let len = (rgba_stride * height) as usize + (width * 4) as usize;
let mut rgba = vec![0u8; len];
let range = yuv::YuvRange::Limited;
let matrix = yuv::YuvStandardMatrix::Bt601;

match chroma {
Chroma::C420 => yuv::yuv420_to_rgba(
&planar_image,
&mut *rgba,
rgba_stride,
range,
matrix,
),
Chroma::C422 => yuv::yuv422_to_rgba(
&planar_image,
&mut *rgba,
rgba_stride,
range,
matrix,
),
Chroma::C444 => yuv::yuv444_to_rgba(
&planar_image,
&mut *rgba,
rgba_stride,
range,
matrix,
),
}?;
DynamicImage::ImageRgba8(
image::RgbaImage::from_vec(width, height, rgba)
.context("RGBA8 image from HEIC")?,
)
}
Some(ColorSpace::Monochrome) => {
let planes = img.planes();
let y = planes.y.context("Grayscale image is missing luma")?;
let buf = y.data.to_vec();
let width = img.width();
let height = img.height();

let i = DynamicImage::ImageLuma8(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Monochrome used in HEIC is YUV image with only Y plane, so it's required to handle YUV range and YUV matrix as well and image also could be 10 bit.

GrayImage::from_vec(width, height, buf)
.context("Grayscale image from HEIC")?,
)
.into_rgb8();
DynamicImage::ImageRgb8(i)
}
_ => {
let planes = img.planes();
let buf = planes
.interleaved
.context("HEIC image is missing data")?
.data
.to_vec();
let width = img.width();
let height = img.height();
DynamicImage::ImageRgba8(
image::RgbaImage::from_vec(width, height, buf)
.context("Unknown image from HEIC")?,
)
}
};

_ = sender.send(Frame::new_still(i));
return Ok(receiver);
Expand Down
Loading