- Updated versions of dependencies.

- Added test for creating TypedImage from slice with using `bytemuck`.
This commit is contained in:
Kirill Kuzminykh
2025-11-28 00:44:25 +03:00
parent d3606255b7
commit 201351d686
7 changed files with 450 additions and 560 deletions
+7
View File
@@ -1,3 +1,10 @@
## [Unreleased] - ReleaseDate
### Added
- Added `bytemuck` feature and implemented some `bytemuck` traits for the `Pixel` type
([#56](https://github.com/Cykooz/fast_image_resize/pull/56)).
## [5.3.0] - 2025-09-02
### Added
Generated
+370 -509
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -23,10 +23,10 @@ exclude = ["/data", "/.github"]
cfg-if = "1.0"
num-traits = "0.2.19"
thiserror = "2.0"
document-features = "0.2.11"
document-features = "0.2.12"
# Optional dependencies
image = { version = "0.25.6", optional = true, default-features = false }
bytemuck = { version = "1.23", optional = true }
image = { version = "0.25.9", optional = true, default-features = false }
bytemuck = { version = "1.24", optional = true }
rayon = { version = "1.11", optional = true }
@@ -37,11 +37,11 @@ rayon = { version = "1.11", optional = true }
## type from the `image` crate.
image = ["dep:image", "dep:bytemuck"]
## Enable this feature to implement traits [bytemuck::Pod] and
## [bytemuck::Zeroable] for `Pixel` type.
## [bytemuck::Zeroable] for the `Pixel` type.
bytemuck = ["dep:bytemuck"]
## This feature enables image processing in the ` rayon ` thread pool.
rayon = ["dep:rayon", "resize/rayon", "image/rayon"]
for_testing = ["image", "image/png"]
for_testing = ["image", "image/png", "bytemuck"]
only_u8x4 = [] # This can be used to experiment with the crate's code.
@@ -76,7 +76,7 @@ incremental = true
opt-level = 3
# release build for procmacros - same config as debug build for procmacros
# release build for procmacros - the same config as debug build for procmacros
[profile.release.build-override]
opt-level = 2
debug = false # when possible
+4 -4
View File
@@ -133,7 +133,7 @@ pub(crate) fn precompute_coefficients(
let cur_index = coeffs.len();
let mut ww: f64 = 0.0;
// Optimisation for follow for-cycle:
// Optimization for follow for-cycle:
// (x + 0.5) - in_center => x - (in_center - 0.5) => x - center
let center = in_center - 0.5;
@@ -144,7 +144,7 @@ pub(crate) fn precompute_coefficients(
for x in x_min..x_max {
let w: f64 = filter((x as f64 - center) * recip_filter_scale);
if x == bound_start && w == 0. {
// Don't use zero coefficients at the start of bound;
// Don't use zero coefficients at the start of the bound;
bound_start += 1;
} else {
coeffs.push(w);
@@ -155,7 +155,7 @@ pub(crate) fn precompute_coefficients(
if bound_end <= bound_start || c != 0. {
break;
}
// Don't use zero coefficients at the end of bound;
// Don't use zero coefficients at the end of the bound;
bound_end -= 1;
}
@@ -164,7 +164,7 @@ pub(crate) fn precompute_coefficients(
// The sum of weights must be equal to 1.0.
coeffs[cur_index..].iter_mut().for_each(|w| *w /= ww);
}
// Remaining values should stay empty if they are used despite x_max.
// The remaining values should stay empty if they are used despite x_max.
coeffs.resize(cur_index + window_size, 0.);
bounds.push(Bound {
start: bound_start,
+17 -13
View File
@@ -40,7 +40,7 @@ impl PixelType {
}
}
/// Returns `true` if given buffer is aligned by the alignment of pixel.
/// Returns `true` if the given buffer has the same alignment as the pixel.
pub(crate) fn is_aligned(&self, buffer: &[u8]) -> bool {
match self {
Self::U8 => true,
@@ -64,7 +64,7 @@ pub trait GetCount {
fn count() -> usize;
}
/// Generic type to represent the number of components in single pixel.
/// Generic type to represent the number of components in a single pixel.
pub struct Count<const N: usize>;
impl<const N: usize> GetCount for Count<N> {
@@ -87,7 +87,7 @@ impl<const N: usize> GetCountOfValues for Values<N> {
}
}
/// Information about one component of pixel.
/// Information about one component of a pixel.
pub trait PixelComponent
where
Self: Sized + Copy + Debug + PartialEq + 'static,
@@ -123,7 +123,7 @@ mod private {
pub trait Sealed {}
}
/// Inner trait that provides additional information about pixel type.
/// Inner trait that provides additional information about a pixel type.
///
/// Don't use this trait in your code. You must use the "child"
/// trait [PixelTrait](crate::PixelTrait) instead.
@@ -166,7 +166,7 @@ pub trait InnerPixel:
size_of::<Self>()
}
/// Create slice of pixel's components from slice of pixels
/// Create a slice of pixel's components from a slice of pixels
fn components(buf: &[Self]) -> &[Self::Component] {
let size = buf.len() * Self::count_of_components();
let components_ptr = buf.as_ptr() as *const Self::Component;
@@ -210,22 +210,26 @@ where
// SAFETY: bytemuck derives are tripped by the use of generic arguments.
// However, since Pixel<T, _, _> is repr(C) it introduces no padding after T nor
// imposes any additional limitations on T. Hence, if T is Zeroable, Pixel is
// imposes any additional limitations on T. Hence, if T is Zeroable, Pixel is
// Zeroable.
#[cfg(feature = "bytemuck")]
unsafe impl<T: bytemuck::Zeroable, C, const COUNT_OF_COMPONENTS: usize>
bytemuck::Zeroable for Pixel<T, C, COUNT_OF_COMPONENTS>
unsafe impl<T: bytemuck::Zeroable, C, const COUNT_OF_COMPONENTS: usize> bytemuck::Zeroable
for Pixel<T, C, COUNT_OF_COMPONENTS>
where
T: Sized + Copy + Clone + PartialEq + Default + 'static,
C: PixelComponent {}
C: PixelComponent,
{
}
// SAFETY: As above. If T is Pod than Pixel<T> is Pod.
// SAFETY: As above. If T is Pod, then Pixel<T> is Pod.
#[cfg(feature = "bytemuck")]
unsafe impl<T: bytemuck::Pod, C, const COUNT_OF_COMPONENTS: usize>
bytemuck::Pod for Pixel<T, C, COUNT_OF_COMPONENTS>
unsafe impl<T: bytemuck::Pod, C, const COUNT_OF_COMPONENTS: usize> bytemuck::Pod
for Pixel<T, C, COUNT_OF_COMPONENTS>
where
T: Sized + Copy + Clone + PartialEq + Default + 'static,
C: PixelComponent {}
C: PixelComponent,
{
}
macro_rules! pixel_struct {
($name:ident, $type:tt, $comp_type:tt, $comp_count:literal, $pixel_type:expr, $doc:expr) => {
+18
View File
@@ -244,3 +244,21 @@ fn cropped_image_mut() {
// Bottom border
assert_eq!(dst_pixels[22 * row_size..], black_block);
}
#[cfg(feature = "bytemuck")]
#[test]
fn use_bytemuck_to_create_typed_image_from_slice() {
use bytemuck::{cast_slice, cast_slice_mut};
let width = 64;
let height = 32;
let mut buffer = vec![0u32; 65 * 32];
let pixels_mut: &mut [U8x4] = cast_slice_mut(&mut buffer);
let res = TypedImage::from_pixels_slice(width, height, pixels_mut);
assert!(res.is_ok());
let pixels: &[U8x4] = cast_slice(&buffer);
let res = TypedImageRef::new(width, height, pixels);
assert!(res.is_ok());
}
+28 -28
View File
@@ -396,13 +396,13 @@ mod not_u8x4 {
#[test]
fn downscale_u8() {
type P = U8;
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [2920348]);
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [2939241]);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[2923555],
[2942035],
);
}
}
@@ -410,13 +410,13 @@ mod not_u8x4 {
#[test]
fn upscale_u8() {
type P = U8;
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [1148754010]);
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [1156367834]);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[1148811829],
[1156403174],
);
}
}
@@ -424,13 +424,13 @@ mod not_u8x4 {
#[test]
fn downscale_u8x2() {
type P = U8x2;
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [2920348, 6121802]);
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [2939241, 6121802]);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[2923555, 6122718],
[2942035, 6122718],
);
}
}
@@ -441,14 +441,14 @@ mod not_u8x4 {
P::upscale_test(
ResizeAlg::Nearest,
CpuExtensions::None,
[1146218632, 2364895380],
[1151087734, 2364895380],
);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[1146284886, 2364890085],
[1151139254, 2364890085],
);
}
}
@@ -508,13 +508,13 @@ mod not_u8x4 {
#[test]
fn downscale_u16() {
type P = U16;
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [750529436]);
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [755389581]);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[751401243],
[756141850],
);
}
}
@@ -522,13 +522,13 @@ mod not_u8x4 {
#[test]
fn upscale_u16() {
type P = U16;
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [295229780570]);
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [297147816110]);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[295246940755],
[297159428939],
);
}
}
@@ -539,14 +539,14 @@ mod not_u8x4 {
P::downscale_test(
ResizeAlg::Nearest,
CpuExtensions::None,
[750529436, 1573303114],
[755389581, 1573303114],
);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[751401243, 1573563971],
[756141850, 1573563971],
);
}
}
@@ -557,14 +557,14 @@ mod not_u8x4 {
P::upscale_test(
ResizeAlg::Nearest,
CpuExtensions::None,
[294578188424, 607778112660],
[295819031151, 607778112660],
);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[294597368766, 607776760273],
[295834912005, 607776760273],
);
}
}
@@ -645,13 +645,13 @@ mod not_u8x4 {
#[test]
fn downscale_i32() {
type P = I32;
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [24593724281554]);
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [24753100746016]);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[36889044005199],
[31386938242305],
);
}
}
@@ -659,13 +659,13 @@ mod not_u8x4 {
#[test]
fn upscale_i32() {
type P = I32;
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [9674237252903955]);
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [9737127426058749]);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[11090415545881916],
[10691007188019666],
);
}
}
@@ -674,13 +674,13 @@ mod not_u8x4 {
#[test]
fn downscale_f32() {
type P = F32;
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [28891951209032]);
P::downscale_test(ResizeAlg::Nearest, CpuExtensions::None, [34175415677930]);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[41687319249443],
[40529868772858],
);
}
}
@@ -688,13 +688,13 @@ mod not_u8x4 {
#[test]
fn upscale_f32() {
type P = F32;
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [11165019414549868]);
P::upscale_test(ResizeAlg::Nearest, CpuExtensions::None, [13281344839147584]);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[12506894762090128],
[14128512150723561],
);
}
}
@@ -706,14 +706,14 @@ mod not_u8x4 {
P::downscale_test(
ResizeAlg::Nearest,
CpuExtensions::None,
[28891951209032, 26023210300788],
[34175415677930, 26023210301282],
);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[41687319249443, 29873206892121],
[40529868772858, 29873206894014],
);
}
}
@@ -724,14 +724,14 @@ mod not_u8x4 {
P::upscale_test(
ResizeAlg::Nearest,
CpuExtensions::None,
[9941292360529429, 10060767588318486],
[9944784041015554, 10060767588513729],
);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[10426687457795354, 10465695788378423],
[10424760317415172, 10465695787559382],
);
}
}