Skip to content

Commit

Permalink
Added planar linearizing
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Jul 9, 2024
1 parent 3526d1c commit 36b7d40
Show file tree
Hide file tree
Showing 19 changed files with 435 additions and 311 deletions.
12 changes: 11 additions & 1 deletion 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 @@ -2,7 +2,7 @@ workspace = { members = ["src/app"] }

[package]
name = "colorutils-rs"
version = "0.4.10"
version = "0.4.11"
edition = "2021"
description = "High performance utilities for color format handling and conversion."
readme = "README.md"
Expand All @@ -16,6 +16,7 @@ repository = "https://github.com/awxkee/colorutils-rs"
exclude = ["*.jpg"]

[dependencies]
erydanos = "0.1.0"
half = "2.4.1"

[features]
Expand Down
10 changes: 4 additions & 6 deletions src/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn main() {
println!("HSL {:?}", hsl);
println!("Back RGB {:?}", hsl.to_rgb8());

let img = ImageReader::open("./assets/test_image_2.png")
let img = ImageReader::open("./assets/beach_horizon.jpg")
.unwrap()
.decode()
.unwrap();
Expand All @@ -34,7 +34,7 @@ fn main() {
let mut src_bytes = img.as_bytes();
let width = dimensions.0;
let height = dimensions.1;
let components = 4;
let components = 3;
//
// let mut dst_rgba = vec![];
// dst_rgba.resize(4usize * width as usize * height as usize, 0u8);
Expand All @@ -58,14 +58,13 @@ fn main() {
lab_store.resize(width as usize * components * height as usize, 0f32);
let src_stride = width * components as u32;
let start_time = Instant::now();
rgba_to_linear(
rgb_to_lab(
src_bytes,
src_stride,
&mut lab_store,
store_stride as u32,
width,
height,
TransferFunction::Srgb,
);
let elapsed_time = start_time.elapsed();
// Print the elapsed time in milliseconds
Expand Down Expand Up @@ -93,14 +92,13 @@ fn main() {
// }

let start_time = Instant::now();
linear_to_rgba(
lab_to_srgb(
&lab_store,
store_stride as u32,
&mut dst_slice,
src_stride,
width,
height,
TransferFunction::Srgb,
);

let elapsed_time = start_time.elapsed();
Expand Down
29 changes: 25 additions & 4 deletions src/gamma_curves.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#[inline(always)]
/// Linear transfer function for sRGB
pub fn srgb_to_linear(gamma: f32) -> f32 {
return if gamma < 0f32 {
0f32
Expand All @@ -12,6 +13,7 @@ pub fn srgb_to_linear(gamma: f32) -> f32 {
}

#[inline(always)]
/// Gamma transfer function for sRGB
pub fn srgb_from_linear(linear: f32) -> f32 {
return if linear < 0.0f32 {
0.0f32
Expand All @@ -25,6 +27,7 @@ pub fn srgb_from_linear(linear: f32) -> f32 {
}

#[inline(always)]
/// Linear transfer function for Rec.709
pub fn rec709_to_linear(gamma: f32) -> f32 {
return if gamma < 0.0f32 {
0.0f32
Expand All @@ -38,6 +41,7 @@ pub fn rec709_to_linear(gamma: f32) -> f32 {
}

#[inline(always)]
/// Gamma transfer function for Rec.709
pub fn rec709_from_linear(linear: f32) -> f32 {
return if linear < 0.0f32 {
0.0f32
Expand All @@ -51,26 +55,43 @@ pub fn rec709_from_linear(linear: f32) -> f32 {
}

#[inline(always)]
/// Pure gamma transfer function for gamma 2.2
pub fn pure_gamma_function(x: f32, gamma: f32) -> f32 {
if x <= 0f32 {
return 0f32;
} else if x >= 1f32 {
return 1f32;
} else {
return x.powf(gamma);
}
}

#[inline(always)]
/// Pure gamma transfer function for gamma 2.2
pub fn gamma2p2_from_linear(linear: f32) -> f32 {
linear.powf(1f32 / 2.2f32)
pure_gamma_function(linear, 1f32 / 2.2f32)
}

#[inline(always)]
/// Linear transfer function for gamma 2.2
pub fn gamma2p2_to_linear(gamma: f32) -> f32 {
gamma.powf(2.2f32)
pure_gamma_function(gamma, 2.2f32)
}

#[inline(always)]
/// Pure gamma transfer function for gamma 2.8
pub fn gamma2p8_from_linear(linear: f32) -> f32 {
linear.powf(1f32 / 2.8f32)
pure_gamma_function(linear, 1f32 / 2.8f32)
}

#[inline(always)]
/// Linear transfer function for gamma 2.8
pub fn gamma2p8_to_linear(gamma: f32) -> f32 {
gamma.powf(2.8f32)
pure_gamma_function(gamma, 2.8f32)
}

#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
/// Declares transfer function for transfer components into a linear colorspace and its inverse
pub enum TransferFunction {
/// sRGB Transfer function
Srgb,
Expand Down
4 changes: 4 additions & 0 deletions src/hsl.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use crate::rgb::Rgb;

#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
/// Represents *HSL* (hue, saturation, lightness) colorspace, H ∈ [0, 360f32], s ∈ [0f32, 1f32], v ∈ [0f32, 1f32]
pub struct Hsl {
/// Hue H ∈ [0, 360f32]
pub h: f32,
/// Saturation s ∈ [0, 1f32]
pub s: f32,
/// Lightness v ∈ [0, 1f32]
pub l: f32,
}

Expand Down
4 changes: 4 additions & 0 deletions src/hsv.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use crate::rgb::Rgb;

#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
/// Represents *HSV* (hue, saturation, value) colorspace, H ∈ [0, 360f32], s ∈ [0f32, 1f32], v ∈ [0f32, 1f32]
pub struct Hsv {
/// Hue H ∈ [0, 360f32]
pub h: f32,
/// Saturation s ∈ [0, 1f32]
pub s: f32,
/// Value v ∈ [0, 1f32]
pub v: f32,
}

Expand Down
5 changes: 4 additions & 1 deletion src/lab.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::rgb::Rgb;
use crate::xyz::Xyz;

/// A CIELAB color.
/// Represents CIELAB color space.
#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq)]
pub struct Lab {
/// `l`: lightness component (0 to 100)
pub l: f32,
/// `a`: green (negative) and red (positive) component.
pub a: f32,
/// `b`: blue (negative) and yellow (positive) component
pub b: f32,
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ mod image_xyza_laba;
mod lab;
mod linear_to_image;
mod linear_to_image_u8;
pub mod linear_to_planar;
mod luv;
#[cfg(all(
any(target_arch = "aarch64", target_arch = "arm"),
target_feature = "neon"
))]
mod neon;
pub mod planar_to_linear;
mod rgb;
mod rgb_expand;
mod rgba;
Expand Down
92 changes: 92 additions & 0 deletions src/linear_to_planar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#[cfg(all(
any(target_arch = "aarch64", target_arch = "arm"),
target_feature = "neon"
))]
use crate::neon::linear_to_planar::neon_linear_plane_to_gamma;
use crate::TransferFunction;

#[inline(always)]
fn linear_to_gamma_channels(
src: &[f32],
src_stride: u32,
dst: &mut [u8],
dst_stride: u32,
width: u32,
height: u32,
transfer_function: TransferFunction,
) {
let mut src_offset = 0usize;
let mut dst_offset = 0usize;

let transfer = transfer_function.get_gamma_function();

for _ in 0..height as usize {
let mut _cx = 0usize;

#[cfg(all(
any(target_arch = "aarch64", target_arch = "arm"),
target_feature = "neon"
))]
unsafe {
_cx = neon_linear_plane_to_gamma(
_cx,
src.as_ptr(),
src_offset as u32,
dst.as_mut_ptr(),
dst_offset as u32,
width,
transfer_function,
);
}

let src_ptr = unsafe { (src.as_ptr() as *const u8).add(src_offset) as *const f32 };
let dst_ptr = unsafe { dst.as_mut_ptr().add(dst_offset) };

for x in _cx..width as usize {
let px = x;
let src_slice = unsafe { src_ptr.add(px) };
let pixel = unsafe { src_slice.read_unaligned() }.min(1f32).max(0f32);

let dst = unsafe { dst_ptr.add(px) };
let transferred = transfer(pixel);
let rgb8 = (transferred * 255f32).min(255f32).max(0f32) as u8;

unsafe {
dst.write_unaligned(rgb8);
}
}

src_offset += src_stride as usize;
dst_offset += dst_stride as usize;
}
}

/// This function converts Linear to Plane. This is much more effective than naive direct transformation
///
/// # Arguments
/// * `src` - A slice contains Linear plane data
/// * `src_stride` - Bytes per row for src data.
/// * `dst` - A mutable slice to receive Gamma plane data
/// * `dst_stride` - Bytes per row for dst data
/// * `width` - Image width
/// * `height` - Image height
/// * `transfer_function` - Transfer function from gamma to linear space. If you don't have specific pick `Srgb`
pub fn linear_to_plane(
src: &[f32],
src_stride: u32,
dst: &mut [u8],
dst_stride: u32,
width: u32,
height: u32,
transfer_function: TransferFunction,
) {
linear_to_gamma_channels(
src,
src_stride,
dst,
dst_stride,
width,
height,
transfer_function,
);
}
4 changes: 2 additions & 2 deletions src/luv.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! # Luv
/// Struct representing a color in CIALuv, a.k.a. L\*u\*v\*, color space
/// Struct representing a color in CIE LUV, a.k.a. L\*u\*v\*, color space
#[derive(Debug, Copy, Clone, Default, PartialOrd)]
pub struct Luv {
/// The L\* value (achromatic luminance) of the colour in 0–100 range.
Expand All @@ -22,7 +22,7 @@ pub struct Luv {
pub v: f32,
}

/// Struct representing a color in cylindrical CIELCh(uv) color space
/// Representing a color in cylindrical CIE LCh(uv) color space
#[derive(Debug, Copy, Clone, Default, PartialOrd)]
pub struct LCh {
/// The L\* value (achromatic luminance) of the colour in 0–100 range.
Expand Down
12 changes: 7 additions & 5 deletions src/neon/cie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ use crate::luv::{
LUV_CUTOFF_FORWARD_Y, LUV_MULTIPLIER_FORWARD_Y, LUV_MULTIPLIER_INVERSE_Y, LUV_WHITE_U_PRIME,
LUV_WHITE_V_PRIME,
};
use crate::neon::math::{
prefer_vfmaq_f32, vatan2q_f32, vcbrtq_f32, vcolorq_matrix_f32, vcosq_f32, vcubeq_f32,
vhypotq_f32, vsinq_f32,
};
use crate::neon::math::{prefer_vfmaq_f32, vcolorq_matrix_f32, vcubeq_f32};
use erydanos::neon::atan2f::vatan2q_f32;
use erydanos::neon::cbrtf::vcbrtq_f32;
use erydanos::neon::cosf::vcosq_f32;
use erydanos::neon::hypotf::vhypotq_fast_f32;
use erydanos::neon::sinf::vsinq_f32;
use std::arch::aarch64::*;

#[inline(always)]
Expand Down Expand Up @@ -100,7 +102,7 @@ pub(crate) unsafe fn neon_triple_to_lch(
z: float32x4_t,
) -> (float32x4_t, float32x4_t, float32x4_t) {
let (luv_l, luv_u, luv_v) = neon_triple_to_luv(x, y, z);
let lch_c = vhypotq_f32(luv_u, luv_v);
let lch_c = vhypotq_fast_f32(luv_u, luv_v);
let lch_h = vatan2q_f32(luv_v, luv_u);
(luv_l, lch_c, lch_h)
}
Expand Down
Loading

0 comments on commit 36b7d40

Please sign in to comment.