mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 10:30:17 +00:00
Implement splash screen feature.
This commit is contained in:
302
deps/moxcms/src/conversions/mba3x4.rs
vendored
Normal file
302
deps/moxcms/src/conversions/mba3x4.rs
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* // Copyright (c) Radzivon Bartoshyk 3/2025. All rights reserved.
|
||||
* //
|
||||
* // Redistribution and use in source and binary forms, with or without modification,
|
||||
* // are permitted provided that the following conditions are met:
|
||||
* //
|
||||
* // 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* // list of conditions and the following disclaimer.
|
||||
* //
|
||||
* // 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* // this list of conditions and the following disclaimer in the documentation
|
||||
* // and/or other materials provided with the distribution.
|
||||
* //
|
||||
* // 3. Neither the name of the copyright holder nor the names of its
|
||||
* // contributors may be used to endorse or promote products derived from
|
||||
* // this software without specific prior written permission.
|
||||
* //
|
||||
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
use crate::conversions::mab::{BCurves3, MCurves3};
|
||||
use crate::err::try_vec;
|
||||
use crate::safe_math::SafeMul;
|
||||
use crate::{
|
||||
CmsError, Cube, DataColorSpace, InPlaceStage, InterpolationMethod, LutMultidimensionalType,
|
||||
MalformedSize, Matrix3d, Stage, TransformOptions, Vector3d, Vector4f,
|
||||
};
|
||||
|
||||
struct ACurves3x4Inverse<'a> {
|
||||
curve0: Box<[f32; 65536]>,
|
||||
curve1: Box<[f32; 65536]>,
|
||||
curve2: Box<[f32; 65536]>,
|
||||
curve3: Box<[f32; 65536]>,
|
||||
clut: &'a [f32],
|
||||
grid_size: [u8; 3],
|
||||
interpolation_method: InterpolationMethod,
|
||||
pcs: DataColorSpace,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
struct ACurves3x4InverseOptimized<'a> {
|
||||
clut: &'a [f32],
|
||||
grid_size: [u8; 3],
|
||||
interpolation_method: InterpolationMethod,
|
||||
pcs: DataColorSpace,
|
||||
}
|
||||
|
||||
impl ACurves3x4Inverse<'_> {
|
||||
fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector4f>(
|
||||
&self,
|
||||
src: &[f32],
|
||||
dst: &mut [f32],
|
||||
fetch: Fetch,
|
||||
) -> Result<(), CmsError> {
|
||||
let scale_value = (self.depth as u32 - 1u32) as f32;
|
||||
|
||||
assert_eq!(src.len() / 3, dst.len() / 4);
|
||||
|
||||
for (src, dst) in src.chunks_exact(3).zip(dst.chunks_exact_mut(4)) {
|
||||
let interpolated = fetch(src[0], src[1], src[2]);
|
||||
let a0 = (interpolated.v[0] * scale_value).round().min(scale_value) as u16;
|
||||
let a1 = (interpolated.v[1] * scale_value).round().min(scale_value) as u16;
|
||||
let a2 = (interpolated.v[2] * scale_value).round().min(scale_value) as u16;
|
||||
let a3 = (interpolated.v[3] * scale_value).round().min(scale_value) as u16;
|
||||
let b0 = self.curve0[a0 as usize];
|
||||
let b1 = self.curve1[a1 as usize];
|
||||
let b2 = self.curve2[a2 as usize];
|
||||
let b3 = self.curve3[a3 as usize];
|
||||
dst[0] = b0;
|
||||
dst[1] = b1;
|
||||
dst[2] = b2;
|
||||
dst[3] = b3;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ACurves3x4InverseOptimized<'_> {
|
||||
fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector4f>(
|
||||
&self,
|
||||
src: &[f32],
|
||||
dst: &mut [f32],
|
||||
fetch: Fetch,
|
||||
) -> Result<(), CmsError> {
|
||||
assert_eq!(src.len() / 3, dst.len() / 4);
|
||||
|
||||
for (src, dst) in src.chunks_exact(3).zip(dst.chunks_exact_mut(4)) {
|
||||
let interpolated = fetch(src[0], src[1], src[2]);
|
||||
let b0 = interpolated.v[0];
|
||||
let b1 = interpolated.v[1];
|
||||
let b2 = interpolated.v[2];
|
||||
let b3 = interpolated.v[3];
|
||||
dst[0] = b0;
|
||||
dst[1] = b1;
|
||||
dst[2] = b2;
|
||||
dst[3] = b3;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stage for ACurves3x4Inverse<'_> {
|
||||
fn transform(&self, src: &[f32], dst: &mut [f32]) -> Result<(), CmsError> {
|
||||
let lut = Cube::new_cube(self.clut, self.grid_size);
|
||||
|
||||
// If PCS is LAB then linear interpolation should be used
|
||||
if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
|
||||
return self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z));
|
||||
}
|
||||
|
||||
match self.interpolation_method {
|
||||
#[cfg(feature = "options")]
|
||||
InterpolationMethod::Tetrahedral => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.tetra_vec4(x, y, z))?;
|
||||
}
|
||||
#[cfg(feature = "options")]
|
||||
InterpolationMethod::Pyramid => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.pyramid_vec4(x, y, z))?;
|
||||
}
|
||||
#[cfg(feature = "options")]
|
||||
InterpolationMethod::Prism => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.prism_vec4(x, y, z))?;
|
||||
}
|
||||
InterpolationMethod::Linear => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stage for ACurves3x4InverseOptimized<'_> {
|
||||
fn transform(&self, src: &[f32], dst: &mut [f32]) -> Result<(), CmsError> {
|
||||
let lut = Cube::new_cube(self.clut, self.grid_size);
|
||||
|
||||
// If PCS is LAB then linear interpolation should be used
|
||||
if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
|
||||
return self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z));
|
||||
}
|
||||
|
||||
match self.interpolation_method {
|
||||
#[cfg(feature = "options")]
|
||||
InterpolationMethod::Tetrahedral => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.tetra_vec4(x, y, z))?;
|
||||
}
|
||||
#[cfg(feature = "options")]
|
||||
InterpolationMethod::Pyramid => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.pyramid_vec4(x, y, z))?;
|
||||
}
|
||||
#[cfg(feature = "options")]
|
||||
InterpolationMethod::Prism => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.prism_vec4(x, y, z))?;
|
||||
}
|
||||
InterpolationMethod::Linear => {
|
||||
self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_mba_3x4(
|
||||
mab: &LutMultidimensionalType,
|
||||
lut: &mut [f32],
|
||||
options: TransformOptions,
|
||||
pcs: DataColorSpace,
|
||||
) -> Result<Vec<f32>, CmsError> {
|
||||
if mab.num_input_channels != 3 && mab.num_output_channels != 4 {
|
||||
return Err(CmsError::UnsupportedProfileConnection);
|
||||
}
|
||||
|
||||
const LERP_DEPTH: usize = 65536;
|
||||
const BP: usize = 13;
|
||||
const DEPTH: usize = 8192;
|
||||
|
||||
if mab.b_curves.len() == 3 {
|
||||
let all_curves_linear = mab.b_curves.iter().all(|curve| curve.is_linear());
|
||||
|
||||
if !all_curves_linear {
|
||||
let curves: Result<Vec<_>, _> = mab
|
||||
.b_curves
|
||||
.iter()
|
||||
.map(|c| {
|
||||
c.build_linearize_table::<u16, LERP_DEPTH, BP>()
|
||||
.ok_or(CmsError::InvalidTrcCurve)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let [curve0, curve1, curve2] =
|
||||
curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
|
||||
let b_curves = BCurves3::<DEPTH> {
|
||||
curve0,
|
||||
curve1,
|
||||
curve2,
|
||||
};
|
||||
b_curves.transform(lut)?;
|
||||
}
|
||||
} else {
|
||||
return Err(CmsError::InvalidAtoBLut);
|
||||
}
|
||||
|
||||
if mab.m_curves.len() == 3 {
|
||||
let all_curves_linear = mab.m_curves.iter().all(|curve| curve.is_linear());
|
||||
if !all_curves_linear
|
||||
|| !mab.matrix.test_equality(Matrix3d::IDENTITY)
|
||||
|| mab.bias.ne(&Vector3d::default())
|
||||
{
|
||||
let curves: Result<Vec<_>, _> = mab
|
||||
.m_curves
|
||||
.iter()
|
||||
.map(|c| {
|
||||
c.build_linearize_table::<u16, LERP_DEPTH, BP>()
|
||||
.ok_or(CmsError::InvalidTrcCurve)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let [curve0, curve1, curve2] =
|
||||
curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
|
||||
|
||||
let matrix = mab.matrix.to_f32();
|
||||
let bias = mab.bias.cast();
|
||||
let m_curves = MCurves3 {
|
||||
curve0,
|
||||
curve1,
|
||||
curve2,
|
||||
matrix,
|
||||
bias,
|
||||
inverse: true,
|
||||
depth: DEPTH,
|
||||
};
|
||||
m_curves.transform(lut)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_lut = try_vec![0f32; (lut.len() / 3) * 4];
|
||||
|
||||
if mab.a_curves.len() == 4 && mab.clut.is_some() {
|
||||
let clut = &mab.clut.as_ref().map(|x| x.to_clut_f32()).unwrap();
|
||||
|
||||
let lut_grid = (mab.grid_points[0] as usize)
|
||||
.safe_mul(mab.grid_points[1] as usize)?
|
||||
.safe_mul(mab.grid_points[2] as usize)?
|
||||
.safe_mul(mab.num_output_channels as usize)?;
|
||||
if clut.len() != lut_grid {
|
||||
return Err(CmsError::MalformedClut(MalformedSize {
|
||||
size: clut.len(),
|
||||
expected: lut_grid,
|
||||
}));
|
||||
}
|
||||
|
||||
let grid_size = [mab.grid_points[0], mab.grid_points[1], mab.grid_points[2]];
|
||||
|
||||
let all_curves_linear = mab.a_curves.iter().all(|curve| curve.is_linear());
|
||||
|
||||
if all_curves_linear {
|
||||
let a_curves = ACurves3x4InverseOptimized {
|
||||
clut,
|
||||
grid_size: [mab.grid_points[0], mab.grid_points[1], mab.grid_points[2]],
|
||||
interpolation_method: options.interpolation_method,
|
||||
pcs,
|
||||
};
|
||||
a_curves.transform(lut, &mut new_lut)?;
|
||||
} else {
|
||||
let curves: Result<Vec<_>, _> = mab
|
||||
.a_curves
|
||||
.iter()
|
||||
.map(|c| {
|
||||
c.build_linearize_table::<u16, LERP_DEPTH, BP>()
|
||||
.ok_or(CmsError::InvalidTrcCurve)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let [curve0, curve1, curve2, curve3] =
|
||||
curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
|
||||
|
||||
let a_curves = ACurves3x4Inverse {
|
||||
curve0,
|
||||
curve1,
|
||||
curve2,
|
||||
curve3,
|
||||
clut,
|
||||
grid_size,
|
||||
interpolation_method: options.interpolation_method,
|
||||
depth: DEPTH,
|
||||
pcs,
|
||||
};
|
||||
a_curves.transform(lut, &mut new_lut)?;
|
||||
}
|
||||
} else {
|
||||
return Err(CmsError::UnsupportedProfileConnection);
|
||||
}
|
||||
|
||||
Ok(new_lut)
|
||||
}
|
||||
Reference in New Issue
Block a user